Quick overview

Current status

#library(nCov2019)
library(leaflet)
library(dplyr)
library(ggplot2)
library(plotly)
library(scales)
library(xts)
library(dygraphs)
library(corrplot)
library(lubridate)
library(fmsb)
library(forecast)
COVID<-read.csv("covid_19_data.csv")
COVID_2<-read.csv("COVID19_13-Apr.csv")

Format date:

Date<-as.Date(COVID_2$Date, format="%m/%d/%y") 

COVID_2$Date2<-Date
COVID_updated<-COVID_2 %>% filter(Date2==max(Date2))
leaflet(width = "100%") %>% 
  addProviderTiles("CartoDB.DarkMatter") %>% 
  setView(lng = 0, lat = 10, zoom = 1.5) %>% 
  addCircleMarkers(data = COVID_updated, 
                   lng = ~ Long,
                   lat = ~ Lat,
                   radius = ~ log(Confirmed+1),
                   color = rgb(218/255,65/255,56/255),
                   fillOpacity = ~ ifelse(Confirmed > 0, 1, 0),
                   stroke = FALSE,
                   label = ~ paste(Province.State,",",Country.Region, ": ", Confirmed)
                   )

Current top 10 countries:

COVID_top<-COVID_2 %>% filter(Date2==max(Date2)) %>% 
  group_by(Country.Region) %>% summarise(Total_confirmed=sum(Confirmed)) %>% 
  top_n(10,Total_confirmed) %>% arrange(desc(Total_confirmed))
plot<-ggplot(data=COVID_top
       , aes(x=Total_confirmed,y=reorder(Country.Region,Total_confirmed))) +
  geom_bar(stat ="identity",alpha=0.8,fill="firebrick3") +
  geom_text(aes(label=Total_confirmed), vjust=0.5, hjust=0.9,color="black", size=3.5) +
  scale_x_continuous(labels = comma) +
  labs(title = paste("Top 10 countries with confirmed cases as of ",max(COVID_2$Date2)),
       x = "Confirmed cases",
       y = "Country") +
  theme_minimal()

ggplotly(plot,tooltip = c("x"),width=750)

Time distribution:

COVID_2_Day<- COVID_2 %>% group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed),
                                                        World_deaths=sum(Deaths),
                                                        World_recovered=sum(Recovered))


COVID_Day_confirmed_series<-xts(COVID_2_Day$World_confirmed, order.by=COVID_2_Day$Date2)
COVID_Day_deaths_series<-xts(COVID_2_Day$World_deaths, order.by=COVID_2_Day$Date2)
COVID_Day_recovered_series<-xts(COVID_2_Day$World_recovered, order.by=COVID_2_Day$Date2)

Day_summary<-cbind(COVID_Day_confirmed_series,COVID_Day_deaths_series,COVID_Day_recovered_series)
dygraph(Day_summary, main = "SARS-COV2-outbreak: Total worldwide cases", 
        xlab="Date", ylab="Total cases",width = 750) %>% 
  dySeries("COVID_Day_confirmed_series", "Total cases",drawPoints = TRUE, 
           pointSize = 3, color=rgb(53/255,116/255,199/255)) %>% 
  dySeries("COVID_Day_deaths_series", "Total deaths",drawPoints = TRUE, 
           pointSize = 3, color=rgb(189/255,55/255,48/255)) %>% 
  dySeries("COVID_Day_recovered_series", "Total recovered",drawPoints = TRUE, 
           pointSize = 3, color=rgb(69/255,136/255,51/255)) %>% 
  dyRangeSelector()
New_count<-function(x)
{
  Daily_cases<-numeric(length(x))
  
  for(i in length(x):2)
  {
    Daily_cases[i]=x[i] - x[i-1]
  }
  return(Daily_cases)
}

New_cases<-New_count(COVID_2_Day$World_confirmed)
New_deaths<-New_count(COVID_2_Day$World_deaths)
New_recovered<-New_count(COVID_2_Day$World_recovered)
COVID_New_confirmed_series<-xts(New_cases, order.by=COVID_2_Day$Date2)
COVID_New_deaths_series<-xts(New_deaths, order.by=COVID_2_Day$Date2)
COVID_New_recovered_series<-xts(New_recovered, order.by=COVID_2_Day$Date2)

New_summary<-cbind(COVID_New_confirmed_series,COVID_New_deaths_series,COVID_New_recovered_series)
dygraph(New_summary, main = "SARS-COV2-outbreak: Total worldwide cases", 
        xlab="Date", ylab="Novel coronavirus cases",width = 750) %>% 
  dySeries("COVID_New_confirmed_series", "New cases",drawPoints = TRUE, 
           pointSize = 3, color=rgb(53/255,116/255,199/255)) %>% 
  dySeries("COVID_New_deaths_series", "New deaths",drawPoints = TRUE, 
           pointSize = 3, color=rgb(189/255,55/255,48/255)) %>% 
  dySeries("COVID_New_recovered_series", "New recovered",drawPoints = TRUE, 
           pointSize = 3, color=rgb(69/255,136/255,51/255)) %>% 
  dyRangeSelector()

Team members countries total cases:

COVID_2_Day_Lebanon<- COVID_2 %>% 
  filter(Country.Region %in% c("Lebanon")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))

COVID_2_Day_Chile<- COVID_2 %>% 
  filter(Country.Region %in% c("Chile")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))

COVID_2_Day_Colombia<- COVID_2 %>% 
  filter(Country.Region %in% c("Colombia")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))

COVID_2_Day_CostaRica<- COVID_2 %>% 
  filter(Country.Region %in% c("Costa Rica")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))


COVID_Day_series_Lebanon<-xts(COVID_2_Day_Lebanon$World_confirmed, order.by=COVID_2_Day_Lebanon$Date2)
COVID_Day_series_Chile<-xts(COVID_2_Day_Chile$World_confirmed, order.by=COVID_2_Day_Chile$Date2)
COVID_Day_series_Colombia<-xts(COVID_2_Day_Colombia$World_confirmed, order.by=COVID_2_Day_Colombia$Date2)
COVID_Day_series_CostaRica<-xts(COVID_2_Day_CostaRica$World_confirmed, order.by=COVID_2_Day_CostaRica$Date2)

Our_Countries<-cbind(COVID_Day_series_Lebanon,COVID_Day_series_Chile,COVID_Day_series_Colombia,COVID_Day_series_CostaRica)
dygraph(Our_Countries, main = "SARS-COV2-outbreak: Total cases by country", xlab="Date", ylab="Total cases",width = 750) %>% 
  dySeries("COVID_Day_series_Lebanon", "Lebanon",drawPoints = TRUE, 
           pointSize = 3, color=rgb(0,0,3/255)) %>% 
  dySeries("COVID_Day_series_Chile", "Chile",drawPoints = TRUE, 
           pointSize = 3,color=rgb(120/255,28/255,109/255)) %>% 
  dySeries("COVID_Day_series_Colombia", "Colombia",drawPoints = TRUE, 
           pointSize = 3,color=rgb(237/255,105/255,37/255)) %>% 
  dySeries("COVID_Day_series_CostaRica", "Costa Rica",drawPoints = TRUE,
           pointSize = 3,color=rgb(204/255,197/255,126/255)) %>% 
  dyRangeSelector()
New_Lebanon<-New_count(COVID_2_Day_Lebanon$World_confirmed)
New_Chile<-New_count(COVID_2_Day_Chile$World_confirmed)
New_Colombia<-New_count(COVID_2_Day_Colombia$World_confirmed)
New_CostaRica<-New_count(COVID_2_Day_CostaRica$World_confirmed)

COVID_New_series_Lebanon<-xts(New_Lebanon, order.by=COVID_2_Day_Lebanon$Date2)
COVID_New_series_Chile<-xts(New_Chile, order.by=COVID_2_Day_Chile$Date2)
COVID_New_series_Colombia<-xts(New_Colombia, order.by=COVID_2_Day_Colombia$Date2)
COVID_New_series_CostaRica<-xts(New_CostaRica, order.by=COVID_2_Day_CostaRica$Date2)

Our_New_Countries<-cbind(COVID_New_series_Lebanon,COVID_New_series_Chile,COVID_New_series_Colombia,COVID_New_series_CostaRica)
dygraph(Our_New_Countries, main = "SARS-COV2-outbreak: New cases by country", xlab="Date", ylab="Total cases",width = 750) %>% 
  dySeries("COVID_New_series_Lebanon", "Lebanon",drawPoints = TRUE, 
           pointSize = 3, color=rgb(0,0,3/255)) %>% 
  dySeries("COVID_New_series_Chile", "Chile",drawPoints = TRUE, 
           pointSize = 3,color=rgb(120/255,28/255,109/255)) %>% 
  dySeries("COVID_New_series_Colombia", "Colombia",drawPoints = TRUE, 
           pointSize = 3,color=rgb(237/255,105/255,37/255)) %>% 
  dySeries("COVID_New_series_CostaRica", "Costa Rica",drawPoints = TRUE,
           pointSize = 3,color=rgb(204/255,197/255,126/255)) %>% 
  dyRangeSelector()

Looking for correlations

fig <- plot_ly(COVID_updated, x = ~Confirmed, y = ~Deaths, z = ~Recovered, width=750) %>% 
  add_markers(text= ~Country.Region ,hoverinfo= "text",
              marker = list(color=rgb(189/255,55/255,48/255))) %>% 
  layout(title="Confirmed cases Vs. Deaths Vs. Recovered", scene = list(
                    xaxis = list(title = 'Confirmed'),
                     yaxis = list(title = 'Deaths'),
                     zaxis = list(title = 'Recovered'))) 
fig

For the number of cases

Human Development Index

HDI<-read.csv("Human Development Index (HDI)_2.csv",sep=";",dec=",")
COVID_Country<-COVID_2 %>% filter(Date2==max(Date2)) %>% 
  group_by(Country.Region) %>% summarise(Total_confirmed=sum(Confirmed),
                                         Total_deaths=sum(Deaths),
                                         Total_Recovered=sum(Recovered))

Remove after parentheses:

HDI$Country_2<-gsub("\\s*\\([^\\)]+\\)","",as.character(HDI$Country))
HDI$Country_2[HDI$Country_2=="United States"]<-"US"
HDI$Country_2[HDI$Country_2=="Korea"]<-"South Korea"

Population:

Population<-read.csv("World_population.csv",sep=";",dec=",")

Remove after commma:

Population$Country_Name_2<-gsub(",.*", "", as.character(Population$Country_Name))
Population$Country_Name_2[Population$Country_Name_2=="United States"]<-"US"
Population$Country_Name_2[Population$Country_Code=="KOR"]<-"South Korea"
Population$Country_Name_2[Population$Country_Code=="CZE"]<-"Czechia"

Natural Join:

COVID_3<- COVID_Country %>% inner_join(HDI,by=c("Country.Region"="Country_2")) %>% 
  inner_join(Population,by=c("Country.Region"="Country_Name_2")) %>% 
  select(Country.Region,Total_confirmed,Total_deaths,Total_Recovered,HDI_Rank_2018,Year_2018,
         Country_Code,Population_2018) %>% 
  mutate(Cases_million=(Total_confirmed/Population_2018)*1000000,
         Recovered_percentage=(Total_Recovered/Total_confirmed)*100)  

COVID_3<-COVID_3[!is.na(COVID_3$Population_2018),]

Plot the Human Development Index(HDI) Vs. the number of cases (applying a log transformation), and the proportion of recovered cases:

plot<-ggplot(data=COVID_3,aes(x=log(Cases_million),y=Year_2018,
                        size=Recovered_percentage,text=Country.Region)) +
  geom_point(color="black",fill=rgb(237/255,105/255,37/255),shape=21,alpha=0.6) +
  scale_size(range = c(3,15), name="Recovered \n percentage") +
  theme_minimal() + 
  theme(legend.position="bottom") +
  labs(title="HDI Vs. logarithmus of COVID-19 cases by million inhabitants \n and proportion of recovered",
       x="ln(Cases/1M population)",
       y="HDI")

ggplotly(plot,tooltip = c("text"),width=750)
COVID_numeric_1<-COVID_3 %>% mutate(Log_cases=log(Cases_million),
                                    Death_percentage=(Total_deaths/Total_confirmed)*100) %>% 
  select(Log_cases,Recovered_percentage,Death_percentage,Year_2018)

corrplot(cor(COVID_numeric_1),method = "number",tl.col="black",tl.srt=15,
         col=colorRampPalette(c(rgb(204/255,197/255,126/255),rgb(237/255,105/255,37/255)))(200))

Measles<-read.csv("Measles_immunization.csv",sep=";",dec=".")
Measles$Country_2<-gsub("\\s*\\([^\\)]+\\)","",as.character(Measles$Country))
Measles$Country_2[Measles$Country_2=="United States"]<-"US"
Measles$Country_2[Measles$Country_2=="Korea"]<-"South Korea"
COVID_3<- COVID_3 %>% inner_join(Measles,by=c("Country.Region"="Country_2")) %>% select(-c("Country"))

Health expenditure (% of GDP)

Health_expenditure<-read.csv("Health_expenditure_GDP.csv",sep=";",dec=".")
Health_expenditure$Country_2<-gsub("\\s*\\([^\\)]+\\)","",
                                   as.character(Health_expenditure$Country))
Health_expenditure$Country_2[Health_expenditure$Country_2=="United States"]<-"US"
Health_expenditure$Country_2[Health_expenditure$Country_2=="Korea"]<-"South Korea"
COVID_3<- COVID_3 %>% inner_join(Health_expenditure,by=c("Country.Region"="Country_2")) %>% select(-c("Country"))
plot<-ggplot(data=COVID_3,aes(x=log(Cases_million),y=Expenditure_2016,
                        size=Recovered_percentage,text=Country.Region)) +
  geom_point(color="black",fill=rgb(204/255,197/255,126/255),shape=21,alpha=0.6) +
  scale_size(range = c(3,15), name="Recovered \n percentage") +
  theme_minimal() + 
  theme(legend.position="bottom") +
  labs(title="Health expenditure (% of GDP) Vs. logarithmus of COVID-19 cases by million inhabitants \n and proportion of recovered",
       x="ln(Cases/1M population)",
       y="% of GDP in health")

ggplotly(plot,tooltip = c("text"),width=750)

Temperature

Temperature<-read.csv("GlobalLandTemperaturesByCountry.csv",sep=";")
Date_Temp<-as.Date(Temperature$dt, format="%d/%m/%Y") 

Temperature$Date_Temp<-Date_Temp

Extract month, filter IQ (Jan, Feb and Mar) and obtain the average IQ temperature:

Temperature<- Temperature %>% mutate(Month=month(Temperature$Date_Temp,label =TRUE),
                                     Year=year(Temperature$Date_Temp)) %>% 
  filter(Month %in% c("Jan","Feb","Mar") & Year>=2000)  %>% 
  group_by(Country) %>% summarise(Avg_IQ_temperature=mean(AverageTemperature))

Temperature<-Temperature[!is.na(Temperature$Avg_IQ_temperature),]
Temperature$Country_2<-gsub("\\s*\\([^\\)]+\\)","",
                                   as.character(Temperature$Country))
Temperature$Country_2[Temperature$Country_2=="United States"]<-"US"
Temperature$Country_2[Temperature$Country_2=="Korea"]<-"South Korea"
COVID_3<- COVID_3 %>% inner_join(Temperature,by=c("Country.Region"="Country_2")) %>% select(-c("Country"))

#write.csv(COVID_3,"COVID_Covariables.csv")
plot<-ggplot(data=COVID_3,aes(x=log(Cases_million),y=Avg_IQ_temperature,
                        size=Recovered_percentage,text=Country.Region)) +
  geom_point(color="black",fill=rgb(120/255,28/255,109/255),shape=21,alpha=0.6) +
  scale_size(range = c(3,15), name="Recovered \n percentage") +
  theme_minimal() + 
  theme(legend.position="bottom") +
  labs(title="Average IQ temperature Vs. logarithmus of COVID-19 cases by million inhabitants \n and proportion of recovered",
       x="ln(Cases/1M population)",
       y="Temperature (Celcius)")

ggplotly(plot,tooltip = c("text"),width=750)

For the number of deaths

###DTP immunization

DTP_immunization<-read.csv("Infants_lacking_immunization_DTP.csv",sep=";")

Remove after parentheses:

DTP_immunization$Country_2<-gsub("\\s*\\([^\\)]+\\)","",
                                 as.character(DTP_immunization$Country))
DTP_immunization$Country_2[DTP_immunization$Country_2=="United States"]<-"US"
DTP_immunization$Country_2[DTP_immunization$Country_2=="Korea"]<-"South Korea"
COVID_4<- COVID_Country %>% inner_join(DTP_immunization,
                                       by=c("Country.Region"="Country_2")) %>% 
  select(Country.Region,Total_confirmed,Total_deaths,Total_Recovered,
         Lack_DTP_inmmunization_2018) %>% 
  mutate(Recovered_percentage=(Total_Recovered/Total_confirmed)*100,
         Death_rate=(Total_deaths/Total_confirmed)*100)
plot<-ggplot(data=COVID_4,aes(x=Death_rate,y=Lack_DTP_inmmunization_2018,
                        size=Recovered_percentage,text=Country.Region)) +
  geom_point(color="black",fill=rgb(120/255,28/255,109/255),shape=21,alpha=0.6) +
  scale_size(range = c(3,15), name="Recovered \n percentage") +
  theme_minimal() + 
  theme(legend.position="bottom") +
  labs(title="% infants lacking DTP immunization Vs. death rate and \n proportion of recovered",
       x="Novel coronavirus death rate",
       y="% of infants")

ggplotly(plot,tooltip = c("text"),width=750)

Infants lacking immunization, measles (% of one-year-olds)

COVID_4<- COVID_4 %>% inner_join(Measles,by=c("Country.Region"="Country_2")) %>% select(-c("Country"))
ggplot(COVID_4, aes(y=Measles_2018)) + 
  geom_boxplot(fill="dodgerblue4",outlier.shape = 21, 
               outlier.fill = "firebrick",alpha=0.75) +
  ggtitle("Boxplot of % infants lacking measles immunization") + ylab("% of infants") +
  theme_minimal()

ggplot(COVID_4, aes(Measles_2018)) + 
  geom_histogram(fill="dodgerblue4",bins=20,alpha=0.8) +
  ggtitle("Histogram of % infants lacking measles immunization") + 
  xlab("% of infants") + 
  ylab("Count") +
  theme_minimal()

plot<-ggplot(data=COVID_4,aes(x=Death_rate,y=Measles_2018,
                        size=Recovered_percentage,text=Country.Region)) +
  geom_point(color="black",fill=rgb(237/255,105/255,37/255),shape=21,alpha=0.6) +
  scale_size(range = c(3,15), name="Recovered \n percentage") +
  theme_minimal() + 
  theme(legend.position="bottom") +
  labs(title="% infants lacking measles immunization Vs. fatality rate \n and proportion of recovered",
       x="Fatality rate (%)",
       y="% of infants")

ggplotly(plot,tooltip = c("text"),width=750)

Fitting a regression model

Transforming with ln and converting temperature as kelvin:

Mod1<-lm(log(COVID_3$Cases_million)~log(COVID_3$Year_2018)+log(COVID_3$Measles_2018)+
           log(COVID_3$Expenditure_2016)+log(COVID_3$Avg_IQ_temperature+273.15))
summary(Mod1)

Call:
lm(formula = log(COVID_3$Cases_million) ~ log(COVID_3$Year_2018) + 
    log(COVID_3$Measles_2018) + log(COVID_3$Expenditure_2016) + 
    log(COVID_3$Avg_IQ_temperature + 273.15))

Residuals:
    Min      1Q  Median      3Q     Max 
-4.0421 -0.8214 -0.0286  0.7862  5.0832 

Coefficients:
                                         Estimate Std. Error t value Pr(>|t|)    
(Intercept)                              30.27716   18.89089   1.603  0.11118    
log(COVID_3$Year_2018)                    7.78377    0.66092  11.777  < 2e-16 ***
log(COVID_3$Measles_2018)                 0.04824    0.11891   0.406  0.68560    
log(COVID_3$Expenditure_2016)             0.98256    0.31153   3.154  0.00196 ** 
log(COVID_3$Avg_IQ_temperature + 273.15) -4.47235    3.32510  -1.345  0.18073    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.373 on 144 degrees of freedom
Multiple R-squared:  0.7089,    Adjusted R-squared:  0.7008 
F-statistic: 87.67 on 4 and 144 DF,  p-value: < 2.2e-16

Stepwise with AIC critertion:

Mod2<-step(Mod1,direction = "both")
Start:  AIC=99.38
log(COVID_3$Cases_million) ~ log(COVID_3$Year_2018) + log(COVID_3$Measles_2018) + 
    log(COVID_3$Expenditure_2016) + log(COVID_3$Avg_IQ_temperature + 
    273.15)

                                           Df Sum of Sq    RSS    AIC
- log(COVID_3$Measles_2018)                 1     0.310 271.77  97.55
- log(COVID_3$Avg_IQ_temperature + 273.15)  1     3.410 274.87  99.24
<none>                                                  271.46  99.38
- log(COVID_3$Expenditure_2016)             1    18.753 290.21 107.33
- log(COVID_3$Year_2018)                    1   261.471 532.93 197.89

Step:  AIC=97.55
log(COVID_3$Cases_million) ~ log(COVID_3$Year_2018) + log(COVID_3$Expenditure_2016) + 
    log(COVID_3$Avg_IQ_temperature + 273.15)

                                           Df Sum of Sq    RSS     AIC
- log(COVID_3$Avg_IQ_temperature + 273.15)  1      3.33 275.10  97.366
<none>                                                  271.77  97.550
+ log(COVID_3$Measles_2018)                 1      0.31 271.46  99.380
- log(COVID_3$Expenditure_2016)             1     19.32 291.09 105.783
- log(COVID_3$Year_2018)                    1    316.33 588.10 210.569

Step:  AIC=97.37
log(COVID_3$Cases_million) ~ log(COVID_3$Year_2018) + log(COVID_3$Expenditure_2016)

                                           Df Sum of Sq    RSS     AIC
<none>                                                  275.10  97.366
+ log(COVID_3$Avg_IQ_temperature + 273.15)  1      3.33 271.77  97.550
+ log(COVID_3$Measles_2018)                 1      0.23 274.87  99.240
- log(COVID_3$Expenditure_2016)             1     25.42 300.52 108.535
- log(COVID_3$Year_2018)                    1    455.42 730.52 240.883
summary(Mod2)

Call:
lm(formula = log(COVID_3$Cases_million) ~ log(COVID_3$Year_2018) + 
    log(COVID_3$Expenditure_2016))

Residuals:
    Min      1Q  Median      3Q     Max 
-3.9628 -0.7716  0.0038  0.8270  5.1058 

Coefficients:
                              Estimate Std. Error t value Pr(>|t|)    
(Intercept)                     4.9120     0.6489   7.569  3.9e-12 ***
log(COVID_3$Year_2018)          8.0454     0.5175  15.547  < 2e-16 ***
log(COVID_3$Expenditure_2016)   1.1006     0.2997   3.673 0.000336 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.373 on 146 degrees of freedom
Multiple R-squared:  0.705, Adjusted R-squared:  0.7009 
F-statistic: 174.4 on 2 and 146 DF,  p-value: < 2.2e-16

Normality of residuals:

hist(Mod2$residuals)

shapiro.test(Mod2$residuals)

    Shapiro-Wilk normality test

data:  Mod2$residuals
W = 0.98403, p-value = 0.08201

Prediction power: separate between training and testing:

set.seed(179819)
Sample <- sample(1:length(COVID_3$Cases_million),length(COVID_3$Cases_million)*0.20)
t.testing<- COVID_3[Sample,]
t.training<- COVID_3[-Sample,]

Transform the training and testing variables as before:

t.training<-t.training %>% mutate(Cases_million_log=log(Cases_million),HDI_log=log(Year_2018),
                      GDP_log=log(Expenditure_2016),
                      Temperature_log_kelvin=log(Avg_IQ_temperature+273.15)) 

t.training<-t.training[,14:17]

t.testing<-t.testing %>% mutate(Cases_million_log=log(Cases_million),HDI_log=log(Year_2018),
                      GDP_log=log(Expenditure_2016),
                      Temperature_log_kelvin=log(Avg_IQ_temperature+273.15)) 

t.testing<-t.testing[,14:17]

Fit the same model with training

Mod3<-lm(Cases_million_log~., data=t.training)
summary(Mod3)

Call:
lm(formula = Cases_million_log ~ ., data = t.training)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.2934 -0.8281 -0.0637  0.7031  4.8412 

Coefficients:
                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)             39.0968    20.3361   1.923  0.05699 .  
HDI_log                  7.1043     0.6537  10.867  < 2e-16 ***
GDP_log                  0.9662     0.3512   2.751  0.00689 ** 
Temperature_log_kelvin  -6.0343     3.5784  -1.686  0.09442 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.36 on 116 degrees of freedom
Multiple R-squared:  0.7012,    Adjusted R-squared:  0.6935 
F-statistic: 90.75 on 3 and 116 DF,  p-value: < 2.2e-16

Error functions:

# Residual Sum of Square (RSS)
RSS<-function(Pred,Actual) {
  ss<-sum((Actual-Pred)^2)
  return(ss)
}

# Residual Standard Error (RSE)
RSE<-function(Pred,Real,NumPred) {
  N<-length(Real)-NumPred-1  
  ss<-sqrt((1/N)*RSS(Pred,Real))
  return(ss)
}
# Mean Squared Error 
MSE <- function(Pred,Actual) {
  N<-length(Actual)
  ss<-(1/N)*RSS(Pred,Actual)
  return(ss)
}

# Relative error
RelativeError<-function(Pred,Actual) {
  ss<-sum(abs(Actual-Pred))/sum(abs(Actual))
  return(ss)
}

Prediction:

Pred<-predict(Mod3,t.testing)

Errors:

RSS_Mod3<-RSS(Pred,t.testing$Cases_million_log)
RSE_Mod3<-RSE(Pred,t.testing$Cases_million_log,dim(t.testing)[2]-1)
MSE_Mod3<-MSE(Pred,t.testing$Cases_million_log)
RelativeError_Mod3<-RelativeError(Pred,t.testing$Cases_million_log)

Mod3_Errors<-c(RSS_Mod3,RSE_Mod3,MSE_Mod3,RelativeError_Mod3)

Now, a model without temperature:

t.training <- t.training %>% select(-Temperature_log_kelvin)
t.testing <- t.testing %>% select(-Temperature_log_kelvin)
Mod4<-lm(Cases_million_log~., data=t.training)
summary(Mod4)

Call:
lm(formula = Cases_million_log ~ ., data = t.training)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.2147 -0.7876  0.0089  0.8187  4.8882 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   4.8260     0.7414   6.510 1.96e-09 ***
HDI_log       7.6422     0.5751  13.288  < 2e-16 ***
GDP_log       1.1165     0.3424   3.261  0.00145 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.37 on 117 degrees of freedom
Multiple R-squared:  0.6939,    Adjusted R-squared:  0.6887 
F-statistic: 132.6 on 2 and 117 DF,  p-value: < 2.2e-16

Prediction:

Pred<-predict(Mod4,t.testing)

Errors:

RSS_Mod4<-RSS(Pred,t.testing$Cases_million_log)
RSE_Mod4<-RSE(Pred,t.testing$Cases_million_log,dim(t.testing)[2]-1)
MSE_Mod4<-MSE(Pred,t.testing$Cases_million_log)
RelativeError_Mod4<-RelativeError(Pred,t.testing$Cases_million_log)

Mod4_Errors<-c(RSS_Mod4,RSE_Mod4,MSE_Mod4,RelativeError_Mod4)

Create a radarplot:

Errors<-rbind(Mod3_Errors,Mod4_Errors)

rownames(Errors)<-c("Model with temperature","Model without temperature")

colnames(Errors)<-c("Residual Sum of Square","Residual Standard Error","Mean Squared Error","Relative error")

Errors<-as.data.frame(Errors)

maximum<-apply(Errors,2,max)

minimum<-apply(Errors,2,min)

Errors<-rbind(minimum,Errors)

Errors<-rbind(maximum,Errors)
radarchart(Errors,maxmin=TRUE,axistype=4,axislabcol="slategray4",
           centerzero=FALSE,seg=8,cglcol="gray67",
           pcol=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"),
           plty=1,
           plwd=3,
           title="Error comparison")

legend <-legend(1.5,1, legend=c("With temperature","Without temperature"),
                 seg.len=-1.4,
                 title="Errors",
                 pch=21, 
                 bty="n" ,lwd=3, y.intersp=1, horiz=FALSE,
                 col=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"))

Forecast by country

Republic of Costa Rica

Model with temperature

CostaRica_Temp<-read.csv("CostaRica_Temperature.csv",sep=";")
CostaRica_Temp$Date<-as.Date(CostaRica_Temp$Date,format="%d/%m/%Y")
COVID_2_Day_CostaRica_Temp<-COVID_2_Day_CostaRica %>%
  inner_join(CostaRica_Temp,by=c("Date2"="Date"))

COVID_Day_series_CostaRica_Temp<-xts(COVID_2_Day_CostaRica_Temp$World_confirmed, order.by=COVID_2_Day_CostaRica_Temp$Date2)
COVID_series_CostaRica_Temp_Train<-COVID_Day_series_CostaRica_Temp[1:64] #Until March 25th
COVID_series_CostaRica_Temp_Test<-COVID_Day_series_CostaRica_Temp[65:length(COVID_Day_series_CostaRica_Temp)] #From March 26th onwards

Get the lagged difference:

plot(diff(COVID_series_CostaRica_Temp_Train),type="l",main="1st ") 

Correlograms of first diference:

tsdisplay(diff(COVID_series_CostaRica_Temp_Train))

Arima model: ARIMA(1,2,1)

#Auto Arima for Costa Rica:

ARIMA1_CostaRica<-auto.arima(COVID_series_CostaRica_Temp_Train,
                             xreg = COVID_2_Day_CostaRica_Temp$Temperature_CostaRica[1:64])
summary(ARIMA1_CostaRica)
Series: COVID_series_CostaRica_Temp_Train 
Regression with ARIMA(1,2,1) errors 

Coefficients:
          ar1     ma1     xreg
      -0.7967  0.4328  -0.2134
s.e.   0.1462  0.1840   0.1159

sigma^2 estimated as 7.227:  log likelihood=-147.93
AIC=303.86   AICc=304.57   BIC=312.37

Training set error measures:
                    ME     RMSE      MAE MPE MAPE      MASE         ACF1
Training set 0.4281342 2.581091 1.397242 NaN  Inf 0.4379416 -0.009839234

Predictive error functions:

#Relative error
RE <- function(Fore,Actual) {
  return(sum(abs(Fore-Actual))/abs(sum(Actual)))
}


#MAPE
MAPE<-function(Fore,Actual){
  return(
    mean(abs(Actual-Fore)/abs(Actual))*100
    )
}

# mean squared error (MSE)
MSE<-function(Fore,Actual) {
  N<-length(Actual)
  ss<-sum((Actual-Fore)^2)
  return((1/N)*ss)
}

#PFA
PFA <- function(Fore,Actual) {
  Total<-0
  N<-length(Fore)
  for(i in 1:N) {
    if(Fore[i]>=Actual[i])
      Total<-Total+1      
  }
  return(Total/N)
}

Forecast prediction to compare

Test_CostaRica_Temp<-COVID_2_Day_CostaRica_Temp$Temperature_CostaRica[65:length(COVID_Day_series_CostaRica_Temp)]


P_CRI_1<-forecast(ARIMA1_CostaRica,xreg = Test_CostaRica_Temp)

Calculate errors:

RE_CRI_1<-RE(P_CRI_1$mean,as.vector(COVID_series_CostaRica_Temp_Test))
MAPE_CRI_1<-MAPE(P_CRI_1$mean,as.vector(COVID_series_CostaRica_Temp_Test))
MSE_CRI_1<-MSE(P_CRI_1$mean,as.vector(COVID_series_CostaRica_Temp_Test))
PFA_CRI_1<-PFA(P_CRI_1$mean,as.vector(COVID_series_CostaRica_Temp_Test))

Errors.CRI_1<-c(RE_CRI_1,MAPE_CRI_1,MSE_CRI_1,PFA_CRI_1)
Errors.CRI_1
[1]   0.02889533   3.30422945 196.94131428   0.42105263

Manual MAPE:

dd<-data.frame(Actual=as.vector(COVID_series_CostaRica_Temp_Test),
               Fore=P_CRI_1$mean
)
dd<-dd %>% mutate(Abs_Per_Error=abs(Actual-Fore)/abs(Actual))
mean(dd$Abs_Per_Error)*100
[1] 3.304229

Model without temperature

ARIMA(1,2,0)

#Auto Arima for Costa Rica:

ARIMA2_CostaRica<-auto.arima(COVID_series_CostaRica_Temp_Train)
summary(ARIMA2_CostaRica)
Series: COVID_series_CostaRica_Temp_Train 
ARIMA(1,2,0) 

Coefficients:
          ar1
      -0.4701
s.e.   0.1133

sigma^2 estimated as 7.607:  log likelihood=-150.5
AIC=304.99   AICc=305.19   BIC=309.24

Training set error measures:
                    ME     RMSE      MAE     MPE     MAPE      MASE       ACF1
Training set 0.5145597 2.692615 1.200452 7.86228 21.76863 0.3762612 0.04160954

Forecast prediction to compare

P_CRI_2<-forecast(ARIMA2_CostaRica,h=length(COVID_series_CostaRica_Temp_Test))

Calculate errors:

RE_CRI_2<-RE(P_CRI_2$mean,as.vector(COVID_series_CostaRica_Temp_Test))
MAPE_CRI_2<-MAPE(P_CRI_2$mean,as.vector(COVID_series_CostaRica_Temp_Test))
MSE_CRI_2<-MSE(P_CRI_2$mean,as.vector(COVID_series_CostaRica_Temp_Test))
PFA_CRI_2<-PFA(P_CRI_2$mean,as.vector(COVID_series_CostaRica_Temp_Test))

Errors.CRI_2<-c(RE_CRI_2,MAPE_CRI_2,MSE_CRI_2,PFA_CRI_2)
Errors.CRI_2
[1]   0.02759212   3.26239839 190.14695712   0.36842105
Errors<-rbind(Errors.CRI_1,Errors.CRI_2)

rownames(Errors)<-c("ARIMAX(1,2,1)","ARIMA(1,2,0)")

colnames(Errors)<-c("Relative error","Mean Abs % error","Mean Squared Error","PFA")

Errors<-as.data.frame(Errors)

maximum<-apply(Errors,2,max)

minimum<-apply(Errors,2,min)

Errors<-rbind(minimum,Errors)

Errors<-rbind(maximum,Errors)
radarchart(Errors,maxmin=TRUE,axistype=4,axislabcol="slategray4",
           centerzero=FALSE,seg=8,cglcol="gray67",
           pcol=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"),
           plty=1,
           plwd=3,
           title="Error comparison")

legend <-legend(1.5,1, legend=c("ARIMAX(1,2,1)","ARIMA(1,2,0)"),
                 seg.len=-1.4,
                 title="Errors",
                 pch=21, 
                 bty="n" ,lwd=3, y.intersp=1, horiz=FALSE,
                 col=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"))

Conclusion: Keep model with temperature

Make model with the overall series

#Auto Arima for Costa Rica:

ARIMA_Final_CostaRica<-auto.arima(COVID_Day_series_CostaRica_Temp,
                             xreg = COVID_2_Day_CostaRica_Temp$Temperature_CostaRica)
summary(ARIMA_Final_CostaRica)
Series: COVID_Day_series_CostaRica_Temp 
Regression with ARIMA(1,1,1) errors 

Coefficients:
         ar1      ma1     xreg
      0.9775  -0.3795  -0.1737
s.e.  0.0216   0.1349   0.1794

sigma^2 estimated as 18.62:  log likelihood=-235.89
AIC=479.78   AICc=480.3   BIC=489.41

Training set error measures:
                    ME     RMSE      MAE MPE MAPE      MASE       ACF1
Training set 0.5865101 4.209981 2.336628 NaN  Inf 0.3130776 0.03301146

Final forecast:

Future_CostaRica_Temp<-CostaRica_Temp$Temperature_CostaRica[CostaRica_Temp$Date>max(COVID_2$Date2)]

P_CRI_Final<-forecast(ARIMA_Final_CostaRica,xreg = Future_CostaRica_Temp)
Low_lim_CRI<-data.frame(P_CRI_Final$lower)[,2]
Upp_lim_CRI<-data.frame(P_CRI_Final$upper)[,2]

For making the plot:

##Data periods
per_1 <- as.Date(as.character(COVID_2_Day_CostaRica_Temp$Date2))
per_2 <- seq(as.Date(max(COVID_2_Day_CostaRica_Temp$Date2)+1,format="%Y-%m-%d"), as.Date("2020-04-30",format="%Y-%m-%d"),"1 day")


# Merge forecast + actuals
data <- xts(COVID_Day_series_CostaRica_Temp,order.by=per_1) 
dataNA <- rep(NA, length(data))
A <- cbind(data,dataNA,dataNA,dataNA)


Low_lim_CRI <- xts(Low_lim_CRI,order.by=per_2)
Forecast_CRI <- xts(P_CRI_Final$mean,order.by=per_2)
Upp_lim_CRI <- xts(Upp_lim_CRI,order.by=per_2)
predNA <- rep(NA, length(Forecast_CRI))
B <- cbind(predNA, Low_lim_CRI, Forecast_CRI, Upp_lim_CRI)

all_series_CRI <- data.frame(rbind(as.matrix(A),as.matrix(B)))
colnames(all_series_CRI) <- c('Actual', 'Lower_limit', 'Forecast', 'Upper_limit')
dygraph(all_series_CRI, main="SARS-COV2-outbreak: Total Costa Rica cases",xlab="Date", ylab="Novel coronavirus cases",width = 750)%>%
  dySeries(c('Lower_limit', 'Forecast', 'Upper_limit'),label="Forecast",strokeWidth=2,
           drawPoints = TRUE, pointSize = 2, color=rgb(189/255,44/255,47/255)) %>%
  dySeries("Actual",drawPoints = TRUE, strokeWidth=2, pointSize = 2,
           color=rgb(10/255,44/255,119/255)) %>% 
  dyRangeSelector()

NA

Mexico

Model with temperature

Mexico_Temp<-read.csv("Mexico_Temperature.csv",sep=";")
Mexico_Temp$Date<-as.Date(Mexico_Temp$Date,format="%d/%m/%Y")
COVID_2_Day_Mexico<- COVID_2 %>% 
  filter(Country.Region %in% c("Mexico")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))

COVID_2_Day_Mexico_Temp<-COVID_2_Day_Mexico %>%
  inner_join(Mexico_Temp,by=c("Date2"="Date"))

COVID_Day_series_Mexico_Temp<-xts(COVID_2_Day_Mexico_Temp$World_confirmed, order.by=COVID_2_Day_Mexico_Temp$Date2)
COVID_series_Mexico_Train<-COVID_Day_series_Mexico_Temp[1:74] #Until April 4th
COVID_series_Mexico_Test<-COVID_Day_series_Mexico_Temp[75:length(COVID_Day_series_Mexico_Temp)] #From April 5th onwards

Get the lagged difference:

plot(diff(COVID_series_Mexico_Train),type="l",main="1st ") 

Correlograms of first diference:

tsdisplay(diff(COVID_series_Mexico_Train))

Arima model: ARIMA(2,2,3)

#Auto Arima for Mexico:

#ARIMA1_Mexico<-auto.arima(COVID_series_Mexico_Train,
                             #xreg = COVID_2_Day_Mexico_Temp$Temperature_Mexico[1:74])
ARIMA1_Mexico<-Arima(COVID_series_Mexico_Train,order=c(2,2,3),xreg=COVID_2_Day_Mexico_Temp$Temperature_Mexico[1:74])
summary(ARIMA1_Mexico)
Series: COVID_series_Mexico_Train 
Regression with ARIMA(2,2,3) errors 

Coefficients:
          ar1      ar2     ma1     ma2     ma3    xreg
      -0.9027  -0.7333  1.1212  0.8938  0.6734  0.0623
s.e.   0.1121   0.1592  0.1093  0.2027  0.1153  0.2567

sigma^2 estimated as 123.3:  log likelihood=-273.92
AIC=561.84   AICc=563.59   BIC=577.78

Training set error measures:
                   ME     RMSE      MAE MPE MAPE      MASE       ACF1
Training set 1.716467 10.48796 5.177678 NaN  Inf 0.2239162 -0.1047828

Forecast prediction to compare

Test_Mexico_Temp<-COVID_2_Day_Mexico_Temp$Temperature_Mexico[75:length(COVID_Day_series_Mexico_Temp)]


P_MEX_1<-forecast(ARIMA1_Mexico,xreg = Test_Mexico_Temp)

Calculate errors:

RE_MEX_1<-RE(P_MEX_1$mean,as.vector(COVID_series_Mexico_Test))
MAPE_MEX_1<-MAPE(P_MEX_1$mean,as.vector(COVID_series_Mexico_Test))
MSE_MEX_1<-MSE(P_MEX_1$mean,as.vector(COVID_series_Mexico_Test))
PFA_MEX_1<-PFA(P_MEX_1$mean,as.vector(COVID_series_Mexico_Test))

Errors.MEX_1<-c(RE_MEX_1,MAPE_MEX_1,MSE_MEX_1,PFA_MEX_1)
Errors.MEX_1
[1] 1.889868e-01 1.632085e+01 5.534272e+05 0.000000e+00

Manual MAPE:

dd<-data.frame(Actual=as.vector(COVID_series_Mexico_Test),
               Fore=P_MEX_1$mean
)
dd<-dd %>% mutate(Abs_Per_Error=abs(Actual-Fore)/abs(Actual))
mean(dd$Abs_Per_Error)*100
[1] 16.32085

Model without temperature

ARIMA(2,2,4)

#Auto Arima for Mexico:

ARIMA2_Mexico<-auto.arima(COVID_series_Mexico_Train)
summary(ARIMA2_Mexico)
Series: COVID_series_Mexico_Train 
ARIMA(2,2,4) 

Coefficients:
          ar1      ar2     ma1     ma2     ma3      ma4
      -0.7232  -0.7137  0.8661  0.6669  0.4019  -0.3260
s.e.   0.1415   0.1688  0.1697  0.2135  0.1529   0.1463

sigma^2 estimated as 116.5:  log likelihood=-272.23
AIC=558.45   AICc=560.2   BIC=574.39

Training set error measures:
                   ME     RMSE     MAE      MPE     MAPE      MASE        ACF1
Training set 2.187346 10.19436 4.88144 4.696633 18.33364 0.2111049 -0.05173888

Forecast prediction to compare

P_MEX_2<-forecast(ARIMA2_Mexico,h=length(COVID_series_Mexico_Test))

Calculate errors:

RE_MEX_2<-RE(P_MEX_2$mean,as.vector(COVID_series_Mexico_Test))
MAPE_MEX_2<-MAPE(P_MEX_2$mean,as.vector(COVID_series_Mexico_Test))
MSE_MEX_2<-MSE(P_MEX_2$mean,as.vector(COVID_series_Mexico_Test))
PFA_MEX_2<-PFA(P_MEX_2$mean,as.vector(COVID_series_Mexico_Test))

Errors.MEX_2<-c(RE_MEX_2,MAPE_MEX_2,MSE_MEX_2,PFA_MEX_2)
Errors.MEX_2
[1] 1.939056e-01 1.673569e+01 5.832504e+05 0.000000e+00
Errors<-rbind(Errors.MEX_1,Errors.MEX_2)

rownames(Errors)<-c("ARIMAX(2,2,3)","ARIMA(2,2,4)")

colnames(Errors)<-c("Relative error","Mean Abs % error","Mean Squared Error","PFA")

Errors<-as.data.frame(Errors)

maximum<-apply(Errors,2,max)

minimum<-apply(Errors,2,min)

Errors<-rbind(minimum,Errors)

Errors<-rbind(maximum,Errors)
radarchart(Errors,maxmin=TRUE,axistype=4,axislabcol="slategray4",
           centerzero=FALSE,seg=8,cglcol="gray67",
           pcol=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"),
           plty=1,
           plwd=3,
           title="Error comparison")

legend <-legend(1.5,1, legend=c("ARIMAX(2,2,3)","ARIMA(2,2,4)"),
                 seg.len=-1.4,
                 title="Errors",
                 pch=21, 
                 bty="n" ,lwd=3, y.intersp=1, horiz=FALSE,
                 col=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"))

Conclusion: Keep model with temperature

Make model with the overall series

#Auto Arima for Mexico:

ARIMA_Final_Mexico<-auto.arima(COVID_Day_series_Mexico_Temp,
                             xreg = COVID_2_Day_Mexico_Temp$Temperature_Mexico)
summary(ARIMA_Final_Mexico)
Series: COVID_Day_series_Mexico_Temp 
Regression with ARIMA(2,2,1) errors 

Coefficients:
         ar1     ar2      ma1     xreg
      0.4489  0.5325  -0.8817  -0.2137
s.e.  0.0992  0.0967   0.0531   0.9974

sigma^2 estimated as 618.9:  log likelihood=-373.91
AIC=757.82   AICc=758.62   BIC=769.79

Training set error measures:
                   ME     RMSE      MAE MPE MAPE      MASE       ACF1
Training set 3.719845 23.96071 10.93701 NaN  Inf 0.1924126 -0.0546056

Final forecast:

Future_Mexico_Temp<-Mexico_Temp$Temperature_Mexico[Mexico_Temp$Date>max(COVID_2$Date2)]

P_MEX_Final<-forecast(ARIMA_Final_Mexico,xreg = Future_Mexico_Temp)
Low_lim_MEX<-data.frame(P_MEX_Final$lower)[,2]
Upp_lim_MEX<-data.frame(P_MEX_Final$upper)[,2]

For making the plot:

##Data periods
per_1 <- as.Date(as.character(COVID_2_Day_Mexico_Temp$Date2))
per_2 <- seq(as.Date(max(COVID_2_Day_Mexico_Temp$Date2)+1,format="%Y-%m-%d"), as.Date("2020-04-30",format="%Y-%m-%d"),"1 day")


# Merge forecast + actuals
data <- xts(COVID_Day_series_Mexico_Temp,order.by=per_1) 
dataNA <- rep(NA, length(data))
A <- cbind(data,dataNA,dataNA,dataNA)


Low_lim_MEX <- xts(Low_lim_MEX,order.by=per_2)
Forecast_MEX <- xts(P_MEX_Final$mean,order.by=per_2)
Upp_lim_MEX <- xts(Upp_lim_MEX,order.by=per_2)
predNA <- rep(NA, length(Forecast_MEX))
B <- cbind(predNA, Low_lim_MEX, Forecast_MEX, Upp_lim_MEX)

all_series_MEX <- data.frame(rbind(as.matrix(A),as.matrix(B)))
colnames(all_series_MEX) <- c('Actual', 'Lower_limit', 'Forecast', 'Upper_limit')
dygraph(all_series_MEX, main="SARS-COV2-outbreak: Total Mexico cases",xlab="Date", ylab="Novel coronavirus cases",width = 750)%>%
  dySeries(c('Lower_limit', 'Forecast', 'Upper_limit'),label="Forecast",strokeWidth=2,
           drawPoints = TRUE, pointSize = 2, color=rgb(189/255,44/255,47/255)) %>%
  dySeries("Actual",drawPoints = TRUE, strokeWidth=2, pointSize = 2,
           color=rgb(43/255,102/255,73/255)) %>% 
  dyRangeSelector()

NA

Italy

Model with temperature

Italy_Temp<-read.csv("Italy_Temperature.csv",sep=";")
Italy_Temp$Date<-as.Date(Italy_Temp$Date,format="%d/%m/%Y")
COVID_2_Day_Italy<- COVID_2 %>% 
  filter(Country.Region %in% c("Italy")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))

COVID_2_Day_Italy_Temp<-COVID_2_Day_Italy %>%
  inner_join(Italy_Temp,by=c("Date2"="Date"))

COVID_Day_series_Italy_Temp<-xts(COVID_2_Day_Italy_Temp$World_confirmed, order.by=COVID_2_Day_Italy_Temp$Date2)
COVID_Day_series_Italy_Train<-COVID_Day_series_Italy_Temp[1:64] #Until March 25th
COVID_Day_series_Italy_Test<-COVID_Day_series_Italy_Temp[65:length(COVID_Day_series_Italy_Temp)] #From March 26th onwards

Get the lagged difference:

plot(diff(COVID_Day_series_Italy_Train),type="l",main="1st ") 

Correlograms of first diference:

tsdisplay(diff(COVID_Day_series_Italy_Train))

Arima model: ARIMA(1,2,0)

#Auto Arima for Italy:

ARIMA1_Italy<-auto.arima(COVID_Day_series_Italy_Train,
                             xreg = COVID_2_Day_Italy_Temp$Temperature_Italy[1:64])
#ARIMA1_Mexico<-Arima(COVID_series_Mexico_Train,order=c(2,2,3),xreg=COVID_2_Day_Mexico_Temp$Temperature_Mexico[1:74])
summary(ARIMA1_Italy)
Series: COVID_Day_series_Italy_Train 
Regression with ARIMA(1,2,0) errors 

Coefficients:
          ar1     xreg
      -0.5094  21.4065
s.e.   0.1119  20.2612

sigma^2 estimated as 489396:  log likelihood=-493.24
AIC=992.47   AICc=992.89   BIC=998.85

Training set error measures:
                   ME     RMSE      MAE MPE MAPE      MASE         ACF1
Training set 123.6808 677.3545 335.2238 NaN  Inf 0.2839123 -0.005148955

Forecast prediction to compare

Test_Italy_Temp<-COVID_2_Day_Italy_Temp$Temperature_Italy[65:length(COVID_Day_series_Italy_Temp)]


P_ITA_1<-forecast(ARIMA1_Italy,xreg = Test_Italy_Temp)

Calculate errors:

RE_ITA_1<-RE(P_ITA_1$mean,as.vector(COVID_Day_series_Italy_Test))
MAPE_ITA_1<-MAPE(P_ITA_1$mean,as.vector(COVID_Day_series_Italy_Test))
MSE_ITA_1<-MSE(P_ITA_1$mean,as.vector(COVID_Day_series_Italy_Test))
PFA_ITA_1<-PFA(P_ITA_1$mean,as.vector(COVID_Day_series_Italy_Test))

Errors.ITA_1<-c(RE_ITA_1,MAPE_ITA_1,MSE_ITA_1,PFA_ITA_1)
Errors.ITA_1
[1] 4.208045e-02 3.728108e+00 4.844373e+07 7.368421e-01

Manual MAPE:

dd<-data.frame(Actual=as.vector(COVID_Day_series_Italy_Test),
               Fore=P_ITA_1$mean
)
dd<-dd %>% mutate(Abs_Per_Error=abs(Actual-Fore)/abs(Actual))
mean(dd$Abs_Per_Error)*100
[1] 3.728108

Model without temperature

ARIMA(1,2,0)

#Auto Arima for Italy:
ARIMA2_Italy<-auto.arima(COVID_Day_series_Italy_Train)
summary(ARIMA2_Italy)
Series: COVID_Day_series_Italy_Train 
ARIMA(1,2,0) 

Coefficients:
          ar1
      -0.5414
s.e.   0.1046

sigma^2 estimated as 489700:  log likelihood=-493.79
AIC=991.58   AICc=991.79   BIC=995.84

Training set error measures:
                   ME     RMSE     MAE      MPE     MAPE      MASE        ACF1
Training set 125.8084 683.1877 295.442 5.335548 11.24576 0.2502197 -0.02348722

Forecast prediction to compare

P_ITA_2<-forecast(ARIMA2_Italy,h=length(COVID_Day_series_Italy_Test))

Calculate errors:

RE_ITA_2<-RE(P_ITA_2$mean,as.vector(COVID_Day_series_Italy_Test))
MAPE_ITA_2<-MAPE(P_ITA_2$mean,as.vector(COVID_Day_series_Italy_Test))
MSE_ITA_2<-MSE(P_ITA_2$mean,as.vector(COVID_Day_series_Italy_Test))
PFA_ITA_2<-PFA(P_ITA_2$mean,as.vector(COVID_Day_series_Italy_Test))

Errors.ITA_2<-c(RE_ITA_2,MAPE_ITA_2,MSE_ITA_2,PFA_ITA_2)
Errors.ITA_2
[1] 3.967952e-02 3.531253e+00 4.311796e+07 6.842105e-01
Errors<-rbind(Errors.ITA_1,Errors.ITA_2)

rownames(Errors)<-c("ARIMAX(1,2,0)","ARIMA(1,2,0)")

colnames(Errors)<-c("Relative error","Mean Abs % error","Mean Squared Error","PFA")

Errors<-as.data.frame(Errors)

maximum<-apply(Errors,2,max)

minimum<-apply(Errors,2,min)

Errors<-rbind(minimum,Errors)

Errors<-rbind(maximum,Errors)
radarchart(Errors,maxmin=TRUE,axistype=4,axislabcol="slategray4",
           centerzero=FALSE,seg=8,cglcol="gray67",
           pcol=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"),
           plty=1,
           plwd=3,
           title="Error comparison")

legend <-legend(1.5,1, legend=c("ARIMAX(1,2,0)","ARIMA(1,2,0)"),
                 seg.len=-1.4,
                 title="Errors",
                 pch=21, 
                 bty="n" ,lwd=3, y.intersp=1, horiz=FALSE,
                 col=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"))

Conclusion: Keep model without temperature

Make model with the overall series

#Auto Arima for Italy:

ARIMA_Final_Italy<-auto.arima(COVID_Day_series_Italy_Temp)
summary(ARIMA_Final_Italy)
Series: COVID_Day_series_Italy_Temp 
ARIMA(1,2,0) 

Coefficients:
          ar1
      -0.4579
s.e.   0.0987

sigma^2 estimated as 494348:  log likelihood=-645.54
AIC=1295.09   AICc=1295.24   BIC=1299.88

Training set error measures:
                   ME    RMSE      MAE     MPE     MAPE     MASE       ACF1
Training set 60.56058 690.275 354.0068 3.79996 8.394382 0.181979 0.00588171

Final forecast:

Future_Italy_Temp<-Italy_Temp$Temperature_Italy[Italy_Temp$Date>max(COVID_2$Date2)]

P_ITA_Final<-forecast(ARIMA_Final_Italy,h=length(Future_Italy_Temp))
Low_lim_ITA<-data.frame(P_ITA_Final$lower)[,2]
Upp_lim_ITA<-data.frame(P_ITA_Final$upper)[,2]

For making the plot:

##Data periods
per_1 <- as.Date(as.character(COVID_2_Day_Italy_Temp$Date2))
per_2 <- seq(as.Date(max(COVID_2_Day_Italy_Temp$Date2)+1,format="%Y-%m-%d"), as.Date("2020-04-30",format="%Y-%m-%d"),"1 day")


# Merge forecast + actuals
data <- xts(COVID_Day_series_Italy_Temp,order.by=per_1) 
dataNA <- rep(NA, length(data))
A <- cbind(data,dataNA,dataNA,dataNA)


Low_lim_ITA <- xts(Low_lim_ITA,order.by=per_2)
Forecast_ITA <- xts(P_ITA_Final$mean,order.by=per_2)
Upp_lim_ITA <- xts(Upp_lim_ITA,order.by=per_2)
predNA <- rep(NA, length(Forecast_ITA))
B <- cbind(predNA, Low_lim_ITA, Forecast_ITA, Upp_lim_ITA)

all_series_ITA <- data.frame(rbind(as.matrix(A),as.matrix(B)))
colnames(all_series_ITA) <- c('Actual', 'Lower_limit', 'Forecast', 'Upper_limit')
dygraph(all_series_ITA, main="SARS-COV2-outbreak: Total Italy cases",xlab="Date", ylab="Novel coronavirus cases",width = 750)%>%
  dySeries(c('Lower_limit', 'Forecast', 'Upper_limit'),label="Forecast",strokeWidth=2,
           drawPoints = TRUE, pointSize = 2, color=rgb(190/255,59/255,61/255)) %>%
  dySeries("Actual",drawPoints = TRUE, strokeWidth=2, pointSize = 2,
           color=rgb(64/255,143/255,78/255)) %>% 
  dyRangeSelector()

NA

Lebanon

Model with temperature

Lebanon_Temp<-read.csv("Lebanon_Temperature.csv",sep=";")
Lebanon_Temp$Date<-as.Date(Lebanon_Temp$Date,format="%d/%m/%Y")
COVID_2_Day_Lebanon<- COVID_2 %>% 
  filter(Country.Region %in% c("Lebanon")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))

COVID_2_Day_Lebanon_Temp<-COVID_2_Day_Lebanon %>%
  inner_join(Lebanon_Temp,by=c("Date2"="Date"))

COVID_Day_series_Lebanon_Temp<-xts(COVID_2_Day_Lebanon_Temp$World_confirmed, order.by=COVID_2_Day_Lebanon_Temp$Date2)
COVID_series_Lebanon_Train<-COVID_Day_series_Lebanon_Temp[1:74] #Until April 4th
COVID_series_Lebanon_Test<-COVID_Day_series_Lebanon_Temp[75:length(COVID_Day_series_Lebanon_Temp)] #From April 4th onwards

Get the lagged difference:

plot(diff(COVID_series_Lebanon_Train),type="l",main="1st ") 

Correlograms of first diference:

tsdisplay(diff(COVID_series_Lebanon_Train))

Arima model: ARIMA(1,1,2)

#Auto Arima for Lebanon:

ARIMA1_Lebanon<-auto.arima(COVID_series_Lebanon_Train,
                             xreg = COVID_2_Day_Lebanon_Temp$Temperature_Lebanon[1:74])
#ARIMA1_Mexico<-Arima(COVID_series_Mexico_Train,order=c(2,2,3),xreg=COVID_2_Day_Mexico_Temp$Temperature_Mexico[1:74])
summary(ARIMA1_Lebanon)
Series: COVID_series_Lebanon_Train 
Regression with ARIMA(1,1,2) errors 

Coefficients:
         ar1      ma1     ma2    xreg
      0.9216  -0.8504  0.4970  0.0461
s.e.  0.0488   0.0989  0.1263  0.2338

sigma^2 estimated as 60.69:  log likelihood=-252.36
AIC=514.72   AICc=515.62   BIC=526.17

Training set error measures:
                   ME     RMSE      MAE MPE MAPE      MASE        ACF1
Training set 1.101941 7.522299 3.444682 NaN  Inf 0.4835803 -0.03853757

Forecast prediction to compare

Test_Lebanon_Temp<-COVID_2_Day_Lebanon_Temp$Temperature_Lebanon[75:length(COVID_Day_series_Lebanon_Temp)]


P_LBN_1<-forecast(ARIMA1_Lebanon,xreg = Test_Lebanon_Temp)

Calculate errors:

RE_LBN_1<-RE(P_LBN_1$mean,as.vector(COVID_series_Lebanon_Test))
MAPE_LBN_1<-MAPE(P_LBN_1$mean,as.vector(COVID_series_Lebanon_Test))
MSE_LBN_1<-MSE(P_LBN_1$mean,as.vector(COVID_series_Lebanon_Test))
PFA_LBN_1<-PFA(P_LBN_1$mean,as.vector(COVID_series_Lebanon_Test))

Errors.LBN_1<-c(RE_LBN_1,MAPE_LBN_1,MSE_LBN_1,PFA_LBN_1)
Errors.LBN_1
[1]   0.03207225   3.07734487 532.24875637   0.33333333

Manual MAPE:

dd<-data.frame(Actual=as.vector(COVID_series_Lebanon_Test),
               Fore=P_LBN_1$mean
)
dd<-dd %>% mutate(Abs_Per_Error=abs(Actual-Fore)/abs(Actual))
mean(dd$Abs_Per_Error)*100
[1] 3.077345

Model without temperature

ARIMA(0,2,2)

#Auto Arima for Lebanon:
ARIMA2_Lebanon<-auto.arima(COVID_series_Lebanon_Train)
summary(ARIMA2_Lebanon)
Series: COVID_series_Lebanon_Train 
ARIMA(0,2,2) 

Coefficients:
          ma1     ma2
      -0.8655  0.4654
s.e.   0.0939  0.1264

sigma^2 estimated as 61.55:  log likelihood=-249.92
AIC=505.84   AICc=506.19   BIC=512.67

Training set error measures:
                   ME     RMSE      MAE      MPE     MAPE      MASE        ACF1
Training set 0.286841 7.630167 3.533028 2.334374 15.75454 0.4959827 -0.05944259

Forecast prediction to compare

P_LBN_2<-forecast(ARIMA2_Lebanon,h=length(COVID_series_Lebanon_Test))

Calculate errors:

RE_LBN_2<-RE(P_LBN_2$mean,as.vector(COVID_series_Lebanon_Test))
MAPE_LBN_2<-MAPE(P_LBN_2$mean,as.vector(COVID_series_Lebanon_Test))
MSE_LBN_2<-MSE(P_LBN_2$mean,as.vector(COVID_series_Lebanon_Test))
PFA_LBN_2<-PFA(P_LBN_2$mean,as.vector(COVID_series_Lebanon_Test))

Errors.LBN_2<-c(RE_LBN_2,MAPE_LBN_2,MSE_LBN_2,PFA_LBN_2)
Errors.LBN_2
[1]  0.01150777  1.15133297 56.61087377  0.55555556
Errors<-rbind(Errors.LBN_1,Errors.LBN_2)

rownames(Errors)<-c("ARIMAX(1,1,2)","ARIMA(0,2,2)")

colnames(Errors)<-c("Relative error","Mean Abs % error","Mean Squared Error","PFA")

Errors<-as.data.frame(Errors)

maximum<-apply(Errors,2,max)

minimum<-apply(Errors,2,min)

Errors<-rbind(minimum,Errors)

Errors<-rbind(maximum,Errors)
radarchart(Errors,maxmin=TRUE,axistype=4,axislabcol="slategray4",
           centerzero=FALSE,seg=8,cglcol="gray67",
           pcol=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"),
           plty=1,
           plwd=3,
           title="Error comparison")

legend <-legend(1.5,1, legend=c("ARIMAX(1,1,2)","ARIMA(0,2,2)"),
                 seg.len=-1.4,
                 title="Errors",
                 pch=21, 
                 bty="n" ,lwd=3, y.intersp=1, horiz=FALSE,
                 col=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"))

Conclusion: Keep model without temperature

Make model with the overall series

#Auto Arima for Lebanon:

ARIMA_Final_Lebanon<-auto.arima(COVID_Day_series_Lebanon_Temp)
summary(ARIMA_Final_Lebanon)
Series: COVID_Day_series_Lebanon_Temp 
ARIMA(2,2,0) 

Coefficients:
          ar1      ar2
      -0.9016  -0.2596
s.e.   0.1070   0.1064

sigma^2 estimated as 62.97:  log likelihood=-282.13
AIC=570.25   AICc=570.57   BIC=577.44

Training set error measures:
                    ME     RMSE      MAE      MPE     MAPE      MASE        ACF1
Training set 0.1748628 7.741674 3.821745 2.495435 12.97023 0.4958593 -0.02103357

Final forecast:

Future_Lebanon_Temp<-Lebanon_Temp$Temperature_Lebanon[Lebanon_Temp$Date>max(COVID_2$Date2)]

P_LBN_Final<-forecast(ARIMA_Final_Lebanon,h=length(Future_Lebanon_Temp))
Low_lim_LBN<-data.frame(P_LBN_Final$lower)[,2]
Upp_lim_LBN<-data.frame(P_LBN_Final$upper)[,2]

For making the plot:

##Data periods
per_1 <- as.Date(as.character(COVID_2_Day_Lebanon_Temp$Date2))
per_2 <- seq(as.Date(max(COVID_2_Day_Lebanon_Temp$Date2)+1,format="%Y-%m-%d"), as.Date("2020-04-30",format="%Y-%m-%d"),"1 day")


# Merge forecast + actuals
data <- xts(COVID_Day_series_Lebanon_Temp,order.by=per_1) 
dataNA <- rep(NA, length(data))
A <- cbind(data,dataNA,dataNA,dataNA)


Low_lim_LBN <- xts(Low_lim_LBN,order.by=per_2)
Forecast_LBN <- xts(P_LBN_Final$mean,order.by=per_2)
Upp_lim_LBN <- xts(Upp_lim_LBN,order.by=per_2)
predNA <- rep(NA, length(Forecast_ITA))
B <- cbind(predNA, Low_lim_LBN, Forecast_LBN, Upp_lim_LBN)

all_series_LBN <- data.frame(rbind(as.matrix(A),as.matrix(B)))
colnames(all_series_LBN) <- c('Actual', 'Lower_limit', 'Forecast', 'Upper_limit')
dygraph(all_series_LBN, main="SARS-COV2-outbreak: Total Lebanon cases",xlab="Date", ylab="Novel coronavirus cases",width = 750)%>%
  dySeries(c('Lower_limit', 'Forecast', 'Upper_limit'),label="Forecast",strokeWidth=2,
           drawPoints = TRUE, pointSize = 2, color=rgb(218/255,55/255,50/255)) %>%
  dySeries("Actual",drawPoints = TRUE, strokeWidth=2, pointSize = 2,
           color=rgb(73/255,163/255,90/255)) %>% 
  dyRangeSelector()

NA
#Forecasts<-list(all_series_CRI,all_series_MEX,all_series_ITA,all_series_LBN)
#names(Forecasts)<-c("Costa Rica","Mexico","Italy","Lebanon")
#Forecasts$Mexico

#save(Forecasts, file="Forecasts.RData")
LS0tCnRpdGxlOiAiQ09WSUQtMTkgT3V0YnJlYWs6IFdvcmxkd2lkZSBhbmFseXNpcyIKYXV0aG9yOiAiQW91biwgQ2FtYXJnbywgTWFydGluZXosUm9kcmlndWV6IgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHRydWUKICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQogICAgdGhlbWU6IGNvc21vCiAgICAgCi0tLQohW10oQ29yb25hdmlydXMuanBnKQoKIyBRdWljayBvdmVydmlldwoKIyMgQ3VycmVudCBzdGF0dXMKCgpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CiNsaWJyYXJ5KG5Db3YyMDE5KQpsaWJyYXJ5KGxlYWZsZXQpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwbG90bHkpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KHh0cykKbGlicmFyeShkeWdyYXBocykKbGlicmFyeShjb3JycGxvdCkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoZm1zYikKbGlicmFyeShmb3JlY2FzdCkKYGBgCgpgYGB7cn0KQ09WSUQ8LXJlYWQuY3N2KCJjb3ZpZF8xOV9kYXRhLmNzdiIpCkNPVklEXzI8LXJlYWQuY3N2KCJDT1ZJRDE5XzEzLUFwci5jc3YiKQpgYGAKCkZvcm1hdCBkYXRlOgpgYGB7cn0KRGF0ZTwtYXMuRGF0ZShDT1ZJRF8yJERhdGUsIGZvcm1hdD0iJW0vJWQvJXkiKSAKCkNPVklEXzIkRGF0ZTI8LURhdGUKYGBgCgpgYGB7cn0KQ09WSURfdXBkYXRlZDwtQ09WSURfMiAlPiUgZmlsdGVyKERhdGUyPT1tYXgoRGF0ZTIpKQpgYGAKCmBgYHtyLHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0KbGVhZmxldCh3aWR0aCA9ICIxMDAlIikgJT4lIAogIGFkZFByb3ZpZGVyVGlsZXMoIkNhcnRvREIuRGFya01hdHRlciIpICU+JSAKICBzZXRWaWV3KGxuZyA9IDAsIGxhdCA9IDEwLCB6b29tID0gMS41KSAlPiUgCiAgYWRkQ2lyY2xlTWFya2VycyhkYXRhID0gQ09WSURfdXBkYXRlZCwgCiAgICAgICAgICAgICAgICAgICBsbmcgPSB+IExvbmcsCiAgICAgICAgICAgICAgICAgICBsYXQgPSB+IExhdCwKICAgICAgICAgICAgICAgICAgIHJhZGl1cyA9IH4gbG9nKENvbmZpcm1lZCsxKSwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gcmdiKDIxOC8yNTUsNjUvMjU1LDU2LzI1NSksCiAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IH4gaWZlbHNlKENvbmZpcm1lZCA+IDAsIDEsIDApLAogICAgICAgICAgICAgICAgICAgc3Ryb2tlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IH4gcGFzdGUoUHJvdmluY2UuU3RhdGUsIiwiLENvdW50cnkuUmVnaW9uLCAiOiAiLCBDb25maXJtZWQpCiAgICAgICAgICAgICAgICAgICApCmBgYAoKQ3VycmVudCB0b3AgMTAgY291bnRyaWVzOgpgYGB7cn0KQ09WSURfdG9wPC1DT1ZJRF8yICU+JSBmaWx0ZXIoRGF0ZTI9PW1heChEYXRlMikpICU+JSAKICBncm91cF9ieShDb3VudHJ5LlJlZ2lvbikgJT4lIHN1bW1hcmlzZShUb3RhbF9jb25maXJtZWQ9c3VtKENvbmZpcm1lZCkpICU+JSAKICB0b3BfbigxMCxUb3RhbF9jb25maXJtZWQpICU+JSBhcnJhbmdlKGRlc2MoVG90YWxfY29uZmlybWVkKSkKYGBgCgpgYGB7cn0KcGxvdDwtZ2dwbG90KGRhdGE9Q09WSURfdG9wCiAgICAgICAsIGFlcyh4PVRvdGFsX2NvbmZpcm1lZCx5PXJlb3JkZXIoQ291bnRyeS5SZWdpb24sVG90YWxfY29uZmlybWVkKSkpICsKICBnZW9tX2JhcihzdGF0ID0iaWRlbnRpdHkiLGFscGhhPTAuOCxmaWxsPSJmaXJlYnJpY2szIikgKwogIGdlb21fdGV4dChhZXMobGFiZWw9VG90YWxfY29uZmlybWVkKSwgdmp1c3Q9MC41LCBoanVzdD0wLjksY29sb3I9ImJsYWNrIiwgc2l6ZT0zLjUpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBsYWJzKHRpdGxlID0gcGFzdGUoIlRvcCAxMCBjb3VudHJpZXMgd2l0aCBjb25maXJtZWQgY2FzZXMgYXMgb2YgIixtYXgoQ09WSURfMiREYXRlMikpLAogICAgICAgeCA9ICJDb25maXJtZWQgY2FzZXMiLAogICAgICAgeSA9ICJDb3VudHJ5IikgKwogIHRoZW1lX21pbmltYWwoKQoKZ2dwbG90bHkocGxvdCx0b29sdGlwID0gYygieCIpLHdpZHRoPTc1MCkKYGBgCgpUaW1lIGRpc3RyaWJ1dGlvbjoKYGBge3J9CkNPVklEXzJfRGF5PC0gQ09WSURfMiAlPiUgZ3JvdXBfYnkoRGF0ZTIpICU+JSBzdW1tYXJpc2UoV29ybGRfY29uZmlybWVkPXN1bShDb25maXJtZWQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdvcmxkX2RlYXRocz1zdW0oRGVhdGhzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXb3JsZF9yZWNvdmVyZWQ9c3VtKFJlY292ZXJlZCkpCgoKQ09WSURfRGF5X2NvbmZpcm1lZF9zZXJpZXM8LXh0cyhDT1ZJRF8yX0RheSRXb3JsZF9jb25maXJtZWQsIG9yZGVyLmJ5PUNPVklEXzJfRGF5JERhdGUyKQpDT1ZJRF9EYXlfZGVhdGhzX3NlcmllczwteHRzKENPVklEXzJfRGF5JFdvcmxkX2RlYXRocywgb3JkZXIuYnk9Q09WSURfMl9EYXkkRGF0ZTIpCkNPVklEX0RheV9yZWNvdmVyZWRfc2VyaWVzPC14dHMoQ09WSURfMl9EYXkkV29ybGRfcmVjb3ZlcmVkLCBvcmRlci5ieT1DT1ZJRF8yX0RheSREYXRlMikKCkRheV9zdW1tYXJ5PC1jYmluZChDT1ZJRF9EYXlfY29uZmlybWVkX3NlcmllcyxDT1ZJRF9EYXlfZGVhdGhzX3NlcmllcyxDT1ZJRF9EYXlfcmVjb3ZlcmVkX3NlcmllcykKYGBgCgpgYGB7cn0KZHlncmFwaChEYXlfc3VtbWFyeSwgbWFpbiA9ICJTQVJTLUNPVjItb3V0YnJlYWs6IFRvdGFsIHdvcmxkd2lkZSBjYXNlcyIsIAogICAgICAgIHhsYWI9IkRhdGUiLCB5bGFiPSJUb3RhbCBjYXNlcyIsd2lkdGggPSA3NTApICU+JSAKICBkeVNlcmllcygiQ09WSURfRGF5X2NvbmZpcm1lZF9zZXJpZXMiLCAiVG90YWwgY2FzZXMiLGRyYXdQb2ludHMgPSBUUlVFLCAKICAgICAgICAgICBwb2ludFNpemUgPSAzLCBjb2xvcj1yZ2IoNTMvMjU1LDExNi8yNTUsMTk5LzI1NSkpICU+JSAKICBkeVNlcmllcygiQ09WSURfRGF5X2RlYXRoc19zZXJpZXMiLCAiVG90YWwgZGVhdGhzIixkcmF3UG9pbnRzID0gVFJVRSwgCiAgICAgICAgICAgcG9pbnRTaXplID0gMywgY29sb3I9cmdiKDE4OS8yNTUsNTUvMjU1LDQ4LzI1NSkpICU+JSAKICBkeVNlcmllcygiQ09WSURfRGF5X3JlY292ZXJlZF9zZXJpZXMiLCAiVG90YWwgcmVjb3ZlcmVkIixkcmF3UG9pbnRzID0gVFJVRSwgCiAgICAgICAgICAgcG9pbnRTaXplID0gMywgY29sb3I9cmdiKDY5LzI1NSwxMzYvMjU1LDUxLzI1NSkpICU+JSAKICBkeVJhbmdlU2VsZWN0b3IoKQpgYGAKCgpgYGB7cn0KTmV3X2NvdW50PC1mdW5jdGlvbih4KQp7CiAgRGFpbHlfY2FzZXM8LW51bWVyaWMobGVuZ3RoKHgpKQogIAogIGZvcihpIGluIGxlbmd0aCh4KToyKQogIHsKICAgIERhaWx5X2Nhc2VzW2ldPXhbaV0gLSB4W2ktMV0KICB9CiAgcmV0dXJuKERhaWx5X2Nhc2VzKQp9CgpOZXdfY2FzZXM8LU5ld19jb3VudChDT1ZJRF8yX0RheSRXb3JsZF9jb25maXJtZWQpCk5ld19kZWF0aHM8LU5ld19jb3VudChDT1ZJRF8yX0RheSRXb3JsZF9kZWF0aHMpCk5ld19yZWNvdmVyZWQ8LU5ld19jb3VudChDT1ZJRF8yX0RheSRXb3JsZF9yZWNvdmVyZWQpCkNPVklEX05ld19jb25maXJtZWRfc2VyaWVzPC14dHMoTmV3X2Nhc2VzLCBvcmRlci5ieT1DT1ZJRF8yX0RheSREYXRlMikKQ09WSURfTmV3X2RlYXRoc19zZXJpZXM8LXh0cyhOZXdfZGVhdGhzLCBvcmRlci5ieT1DT1ZJRF8yX0RheSREYXRlMikKQ09WSURfTmV3X3JlY292ZXJlZF9zZXJpZXM8LXh0cyhOZXdfcmVjb3ZlcmVkLCBvcmRlci5ieT1DT1ZJRF8yX0RheSREYXRlMikKCk5ld19zdW1tYXJ5PC1jYmluZChDT1ZJRF9OZXdfY29uZmlybWVkX3NlcmllcyxDT1ZJRF9OZXdfZGVhdGhzX3NlcmllcyxDT1ZJRF9OZXdfcmVjb3ZlcmVkX3NlcmllcykKYGBgCgpgYGB7cn0KZHlncmFwaChOZXdfc3VtbWFyeSwgbWFpbiA9ICJTQVJTLUNPVjItb3V0YnJlYWs6IFRvdGFsIHdvcmxkd2lkZSBjYXNlcyIsIAogICAgICAgIHhsYWI9IkRhdGUiLCB5bGFiPSJOb3ZlbCBjb3JvbmF2aXJ1cyBjYXNlcyIsd2lkdGggPSA3NTApICU+JSAKICBkeVNlcmllcygiQ09WSURfTmV3X2NvbmZpcm1lZF9zZXJpZXMiLCAiTmV3IGNhc2VzIixkcmF3UG9pbnRzID0gVFJVRSwgCiAgICAgICAgICAgcG9pbnRTaXplID0gMywgY29sb3I9cmdiKDUzLzI1NSwxMTYvMjU1LDE5OS8yNTUpKSAlPiUgCiAgZHlTZXJpZXMoIkNPVklEX05ld19kZWF0aHNfc2VyaWVzIiwgIk5ldyBkZWF0aHMiLGRyYXdQb2ludHMgPSBUUlVFLCAKICAgICAgICAgICBwb2ludFNpemUgPSAzLCBjb2xvcj1yZ2IoMTg5LzI1NSw1NS8yNTUsNDgvMjU1KSkgJT4lIAogIGR5U2VyaWVzKCJDT1ZJRF9OZXdfcmVjb3ZlcmVkX3NlcmllcyIsICJOZXcgcmVjb3ZlcmVkIixkcmF3UG9pbnRzID0gVFJVRSwgCiAgICAgICAgICAgcG9pbnRTaXplID0gMywgY29sb3I9cmdiKDY5LzI1NSwxMzYvMjU1LDUxLzI1NSkpICU+JSAKICBkeVJhbmdlU2VsZWN0b3IoKQpgYGAKCgpUZWFtIG1lbWJlcnMgY291bnRyaWVzIHRvdGFsIGNhc2VzOgpgYGB7cn0KQ09WSURfMl9EYXlfTGViYW5vbjwtIENPVklEXzIgJT4lIAogIGZpbHRlcihDb3VudHJ5LlJlZ2lvbiAlaW4lIGMoIkxlYmFub24iKSkgJT4lIAogIGdyb3VwX2J5KERhdGUyKSAlPiUgc3VtbWFyaXNlKFdvcmxkX2NvbmZpcm1lZD1zdW0oQ29uZmlybWVkKSkKCkNPVklEXzJfRGF5X0NoaWxlPC0gQ09WSURfMiAlPiUgCiAgZmlsdGVyKENvdW50cnkuUmVnaW9uICVpbiUgYygiQ2hpbGUiKSkgJT4lIAogIGdyb3VwX2J5KERhdGUyKSAlPiUgc3VtbWFyaXNlKFdvcmxkX2NvbmZpcm1lZD1zdW0oQ29uZmlybWVkKSkKCkNPVklEXzJfRGF5X0NvbG9tYmlhPC0gQ09WSURfMiAlPiUgCiAgZmlsdGVyKENvdW50cnkuUmVnaW9uICVpbiUgYygiQ29sb21iaWEiKSkgJT4lIAogIGdyb3VwX2J5KERhdGUyKSAlPiUgc3VtbWFyaXNlKFdvcmxkX2NvbmZpcm1lZD1zdW0oQ29uZmlybWVkKSkKCkNPVklEXzJfRGF5X0Nvc3RhUmljYTwtIENPVklEXzIgJT4lIAogIGZpbHRlcihDb3VudHJ5LlJlZ2lvbiAlaW4lIGMoIkNvc3RhIFJpY2EiKSkgJT4lIAogIGdyb3VwX2J5KERhdGUyKSAlPiUgc3VtbWFyaXNlKFdvcmxkX2NvbmZpcm1lZD1zdW0oQ29uZmlybWVkKSkKCgpDT1ZJRF9EYXlfc2VyaWVzX0xlYmFub248LXh0cyhDT1ZJRF8yX0RheV9MZWJhbm9uJFdvcmxkX2NvbmZpcm1lZCwgb3JkZXIuYnk9Q09WSURfMl9EYXlfTGViYW5vbiREYXRlMikKQ09WSURfRGF5X3Nlcmllc19DaGlsZTwteHRzKENPVklEXzJfRGF5X0NoaWxlJFdvcmxkX2NvbmZpcm1lZCwgb3JkZXIuYnk9Q09WSURfMl9EYXlfQ2hpbGUkRGF0ZTIpCkNPVklEX0RheV9zZXJpZXNfQ29sb21iaWE8LXh0cyhDT1ZJRF8yX0RheV9Db2xvbWJpYSRXb3JsZF9jb25maXJtZWQsIG9yZGVyLmJ5PUNPVklEXzJfRGF5X0NvbG9tYmlhJERhdGUyKQpDT1ZJRF9EYXlfc2VyaWVzX0Nvc3RhUmljYTwteHRzKENPVklEXzJfRGF5X0Nvc3RhUmljYSRXb3JsZF9jb25maXJtZWQsIG9yZGVyLmJ5PUNPVklEXzJfRGF5X0Nvc3RhUmljYSREYXRlMikKCk91cl9Db3VudHJpZXM8LWNiaW5kKENPVklEX0RheV9zZXJpZXNfTGViYW5vbixDT1ZJRF9EYXlfc2VyaWVzX0NoaWxlLENPVklEX0RheV9zZXJpZXNfQ29sb21iaWEsQ09WSURfRGF5X3Nlcmllc19Db3N0YVJpY2EpCgpgYGAKCmBgYHtyfQpkeWdyYXBoKE91cl9Db3VudHJpZXMsIG1haW4gPSAiU0FSUy1DT1YyLW91dGJyZWFrOiBUb3RhbCBjYXNlcyBieSBjb3VudHJ5IiwgeGxhYj0iRGF0ZSIsIHlsYWI9IlRvdGFsIGNhc2VzIix3aWR0aCA9IDc1MCkgJT4lIAogIGR5U2VyaWVzKCJDT1ZJRF9EYXlfc2VyaWVzX0xlYmFub24iLCAiTGViYW5vbiIsZHJhd1BvaW50cyA9IFRSVUUsIAogICAgICAgICAgIHBvaW50U2l6ZSA9IDMsIGNvbG9yPXJnYigwLDAsMy8yNTUpKSAlPiUgCiAgZHlTZXJpZXMoIkNPVklEX0RheV9zZXJpZXNfQ2hpbGUiLCAiQ2hpbGUiLGRyYXdQb2ludHMgPSBUUlVFLCAKICAgICAgICAgICBwb2ludFNpemUgPSAzLGNvbG9yPXJnYigxMjAvMjU1LDI4LzI1NSwxMDkvMjU1KSkgJT4lIAogIGR5U2VyaWVzKCJDT1ZJRF9EYXlfc2VyaWVzX0NvbG9tYmlhIiwgIkNvbG9tYmlhIixkcmF3UG9pbnRzID0gVFJVRSwgCiAgICAgICAgICAgcG9pbnRTaXplID0gMyxjb2xvcj1yZ2IoMjM3LzI1NSwxMDUvMjU1LDM3LzI1NSkpICU+JSAKICBkeVNlcmllcygiQ09WSURfRGF5X3Nlcmllc19Db3N0YVJpY2EiLCAiQ29zdGEgUmljYSIsZHJhd1BvaW50cyA9IFRSVUUsCiAgICAgICAgICAgcG9pbnRTaXplID0gMyxjb2xvcj1yZ2IoMjA0LzI1NSwxOTcvMjU1LDEyNi8yNTUpKSAlPiUgCiAgZHlSYW5nZVNlbGVjdG9yKCkKYGBgCgpgYGB7cn0KTmV3X0xlYmFub248LU5ld19jb3VudChDT1ZJRF8yX0RheV9MZWJhbm9uJFdvcmxkX2NvbmZpcm1lZCkKTmV3X0NoaWxlPC1OZXdfY291bnQoQ09WSURfMl9EYXlfQ2hpbGUkV29ybGRfY29uZmlybWVkKQpOZXdfQ29sb21iaWE8LU5ld19jb3VudChDT1ZJRF8yX0RheV9Db2xvbWJpYSRXb3JsZF9jb25maXJtZWQpCk5ld19Db3N0YVJpY2E8LU5ld19jb3VudChDT1ZJRF8yX0RheV9Db3N0YVJpY2EkV29ybGRfY29uZmlybWVkKQoKQ09WSURfTmV3X3Nlcmllc19MZWJhbm9uPC14dHMoTmV3X0xlYmFub24sIG9yZGVyLmJ5PUNPVklEXzJfRGF5X0xlYmFub24kRGF0ZTIpCkNPVklEX05ld19zZXJpZXNfQ2hpbGU8LXh0cyhOZXdfQ2hpbGUsIG9yZGVyLmJ5PUNPVklEXzJfRGF5X0NoaWxlJERhdGUyKQpDT1ZJRF9OZXdfc2VyaWVzX0NvbG9tYmlhPC14dHMoTmV3X0NvbG9tYmlhLCBvcmRlci5ieT1DT1ZJRF8yX0RheV9Db2xvbWJpYSREYXRlMikKQ09WSURfTmV3X3Nlcmllc19Db3N0YVJpY2E8LXh0cyhOZXdfQ29zdGFSaWNhLCBvcmRlci5ieT1DT1ZJRF8yX0RheV9Db3N0YVJpY2EkRGF0ZTIpCgpPdXJfTmV3X0NvdW50cmllczwtY2JpbmQoQ09WSURfTmV3X3Nlcmllc19MZWJhbm9uLENPVklEX05ld19zZXJpZXNfQ2hpbGUsQ09WSURfTmV3X3Nlcmllc19Db2xvbWJpYSxDT1ZJRF9OZXdfc2VyaWVzX0Nvc3RhUmljYSkKYGBgCgpgYGB7cn0KZHlncmFwaChPdXJfTmV3X0NvdW50cmllcywgbWFpbiA9ICJTQVJTLUNPVjItb3V0YnJlYWs6IE5ldyBjYXNlcyBieSBjb3VudHJ5IiwgeGxhYj0iRGF0ZSIsIHlsYWI9IlRvdGFsIGNhc2VzIix3aWR0aCA9IDc1MCkgJT4lIAogIGR5U2VyaWVzKCJDT1ZJRF9OZXdfc2VyaWVzX0xlYmFub24iLCAiTGViYW5vbiIsZHJhd1BvaW50cyA9IFRSVUUsIAogICAgICAgICAgIHBvaW50U2l6ZSA9IDMsIGNvbG9yPXJnYigwLDAsMy8yNTUpKSAlPiUgCiAgZHlTZXJpZXMoIkNPVklEX05ld19zZXJpZXNfQ2hpbGUiLCAiQ2hpbGUiLGRyYXdQb2ludHMgPSBUUlVFLCAKICAgICAgICAgICBwb2ludFNpemUgPSAzLGNvbG9yPXJnYigxMjAvMjU1LDI4LzI1NSwxMDkvMjU1KSkgJT4lIAogIGR5U2VyaWVzKCJDT1ZJRF9OZXdfc2VyaWVzX0NvbG9tYmlhIiwgIkNvbG9tYmlhIixkcmF3UG9pbnRzID0gVFJVRSwgCiAgICAgICAgICAgcG9pbnRTaXplID0gMyxjb2xvcj1yZ2IoMjM3LzI1NSwxMDUvMjU1LDM3LzI1NSkpICU+JSAKICBkeVNlcmllcygiQ09WSURfTmV3X3Nlcmllc19Db3N0YVJpY2EiLCAiQ29zdGEgUmljYSIsZHJhd1BvaW50cyA9IFRSVUUsCiAgICAgICAgICAgcG9pbnRTaXplID0gMyxjb2xvcj1yZ2IoMjA0LzI1NSwxOTcvMjU1LDEyNi8yNTUpKSAlPiUgCiAgZHlSYW5nZVNlbGVjdG9yKCkKYGBgCgojIExvb2tpbmcgZm9yIGNvcnJlbGF0aW9ucwoKYGBge3J9CmZpZyA8LSBwbG90X2x5KENPVklEX3VwZGF0ZWQsIHggPSB+Q29uZmlybWVkLCB5ID0gfkRlYXRocywgeiA9IH5SZWNvdmVyZWQsIHdpZHRoPTc1MCkgJT4lIAogIGFkZF9tYXJrZXJzKHRleHQ9IH5Db3VudHJ5LlJlZ2lvbiAsaG92ZXJpbmZvPSAidGV4dCIsCiAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvcj1yZ2IoMTg5LzI1NSw1NS8yNTUsNDgvMjU1KSkpICU+JSAKICBsYXlvdXQodGl0bGU9IkNvbmZpcm1lZCBjYXNlcyBWcy4gRGVhdGhzIFZzLiBSZWNvdmVyZWQiLCBzY2VuZSA9IGxpc3QoCiAgICAgICAgICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gJ0NvbmZpcm1lZCcpLAogICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAnRGVhdGhzJyksCiAgICAgICAgICAgICAgICAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICdSZWNvdmVyZWQnKSkpIApmaWcKYGBgCiMjIEZvciB0aGUgbnVtYmVyIG9mIGNhc2VzCgojIyMgSHVtYW4gRGV2ZWxvcG1lbnQgSW5kZXgKCmBgYHtyfQpIREk8LXJlYWQuY3N2KCJIdW1hbiBEZXZlbG9wbWVudCBJbmRleCAoSERJKV8yLmNzdiIsc2VwPSI7IixkZWM9IiwiKQpgYGAKCmBgYHtyfQpDT1ZJRF9Db3VudHJ5PC1DT1ZJRF8yICU+JSBmaWx0ZXIoRGF0ZTI9PW1heChEYXRlMikpICU+JSAKICBncm91cF9ieShDb3VudHJ5LlJlZ2lvbikgJT4lIHN1bW1hcmlzZShUb3RhbF9jb25maXJtZWQ9c3VtKENvbmZpcm1lZCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVG90YWxfZGVhdGhzPXN1bShEZWF0aHMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRvdGFsX1JlY292ZXJlZD1zdW0oUmVjb3ZlcmVkKSkKYGBgCgpSZW1vdmUgYWZ0ZXIgcGFyZW50aGVzZXM6CmBgYHtyfQpIREkkQ291bnRyeV8yPC1nc3ViKCJcXHMqXFwoW15cXCldK1xcKSIsIiIsYXMuY2hhcmFjdGVyKEhESSRDb3VudHJ5KSkKYGBgCgpgYGB7cn0KSERJJENvdW50cnlfMltIREkkQ291bnRyeV8yPT0iVW5pdGVkIFN0YXRlcyJdPC0iVVMiCkhESSRDb3VudHJ5XzJbSERJJENvdW50cnlfMj09IktvcmVhIl08LSJTb3V0aCBLb3JlYSIKYGBgCgpQb3B1bGF0aW9uOgpgYGB7cn0KUG9wdWxhdGlvbjwtcmVhZC5jc3YoIldvcmxkX3BvcHVsYXRpb24uY3N2IixzZXA9IjsiLGRlYz0iLCIpCmBgYAoKUmVtb3ZlIGFmdGVyIGNvbW1tYToKYGBge3J9ClBvcHVsYXRpb24kQ291bnRyeV9OYW1lXzI8LWdzdWIoIiwuKiIsICIiLCBhcy5jaGFyYWN0ZXIoUG9wdWxhdGlvbiRDb3VudHJ5X05hbWUpKQpgYGAKCmBgYHtyfQpQb3B1bGF0aW9uJENvdW50cnlfTmFtZV8yW1BvcHVsYXRpb24kQ291bnRyeV9OYW1lXzI9PSJVbml0ZWQgU3RhdGVzIl08LSJVUyIKUG9wdWxhdGlvbiRDb3VudHJ5X05hbWVfMltQb3B1bGF0aW9uJENvdW50cnlfQ29kZT09IktPUiJdPC0iU291dGggS29yZWEiClBvcHVsYXRpb24kQ291bnRyeV9OYW1lXzJbUG9wdWxhdGlvbiRDb3VudHJ5X0NvZGU9PSJDWkUiXTwtIkN6ZWNoaWEiCmBgYAoKTmF0dXJhbCBKb2luOgpgYGB7cix3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9CkNPVklEXzM8LSBDT1ZJRF9Db3VudHJ5ICU+JSBpbm5lcl9qb2luKEhESSxieT1jKCJDb3VudHJ5LlJlZ2lvbiI9IkNvdW50cnlfMiIpKSAlPiUgCiAgaW5uZXJfam9pbihQb3B1bGF0aW9uLGJ5PWMoIkNvdW50cnkuUmVnaW9uIj0iQ291bnRyeV9OYW1lXzIiKSkgJT4lIAogIHNlbGVjdChDb3VudHJ5LlJlZ2lvbixUb3RhbF9jb25maXJtZWQsVG90YWxfZGVhdGhzLFRvdGFsX1JlY292ZXJlZCxIRElfUmFua18yMDE4LFllYXJfMjAxOCwKICAgICAgICAgQ291bnRyeV9Db2RlLFBvcHVsYXRpb25fMjAxOCkgJT4lIAogIG11dGF0ZShDYXNlc19taWxsaW9uPShUb3RhbF9jb25maXJtZWQvUG9wdWxhdGlvbl8yMDE4KSoxMDAwMDAwLAogICAgICAgICBSZWNvdmVyZWRfcGVyY2VudGFnZT0oVG90YWxfUmVjb3ZlcmVkL1RvdGFsX2NvbmZpcm1lZCkqMTAwKSAgCgpDT1ZJRF8zPC1DT1ZJRF8zWyFpcy5uYShDT1ZJRF8zJFBvcHVsYXRpb25fMjAxOCksXQoKYGBgCgpQbG90IHRoZSBIdW1hbiBEZXZlbG9wbWVudCBJbmRleChIREkpIFZzLiB0aGUgbnVtYmVyIG9mIGNhc2VzIChhcHBseWluZyBhIGxvZyB0cmFuc2Zvcm1hdGlvbiksIGFuZCB0aGUgcHJvcG9ydGlvbiBvZiByZWNvdmVyZWQgY2FzZXM6CmBgYHtyfQpwbG90PC1nZ3Bsb3QoZGF0YT1DT1ZJRF8zLGFlcyh4PWxvZyhDYXNlc19taWxsaW9uKSx5PVllYXJfMjAxOCwKICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZT1SZWNvdmVyZWRfcGVyY2VudGFnZSx0ZXh0PUNvdW50cnkuUmVnaW9uKSkgKwogIGdlb21fcG9pbnQoY29sb3I9ImJsYWNrIixmaWxsPXJnYigyMzcvMjU1LDEwNS8yNTUsMzcvMjU1KSxzaGFwZT0yMSxhbHBoYT0wLjYpICsKICBzY2FsZV9zaXplKHJhbmdlID0gYygzLDE1KSwgbmFtZT0iUmVjb3ZlcmVkIFxuIHBlcmNlbnRhZ2UiKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKSArCiAgbGFicyh0aXRsZT0iSERJIFZzLiBsb2dhcml0aG11cyBvZiBDT1ZJRC0xOSBjYXNlcyBieSBtaWxsaW9uIGluaGFiaXRhbnRzIFxuIGFuZCBwcm9wb3J0aW9uIG9mIHJlY292ZXJlZCIsCiAgICAgICB4PSJsbihDYXNlcy8xTSBwb3B1bGF0aW9uKSIsCiAgICAgICB5PSJIREkiKQoKZ2dwbG90bHkocGxvdCx0b29sdGlwID0gYygidGV4dCIpLHdpZHRoPTc1MCkKYGBgCgoKYGBge3J9CkNPVklEX251bWVyaWNfMTwtQ09WSURfMyAlPiUgbXV0YXRlKExvZ19jYXNlcz1sb2coQ2FzZXNfbWlsbGlvbiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERlYXRoX3BlcmNlbnRhZ2U9KFRvdGFsX2RlYXRocy9Ub3RhbF9jb25maXJtZWQpKjEwMCkgJT4lIAogIHNlbGVjdChMb2dfY2FzZXMsUmVjb3ZlcmVkX3BlcmNlbnRhZ2UsRGVhdGhfcGVyY2VudGFnZSxZZWFyXzIwMTgpCgpjb3JycGxvdChjb3IoQ09WSURfbnVtZXJpY18xKSxtZXRob2QgPSAibnVtYmVyIix0bC5jb2w9ImJsYWNrIix0bC5zcnQ9MTUsCiAgICAgICAgIGNvbD1jb2xvclJhbXBQYWxldHRlKGMocmdiKDIwNC8yNTUsMTk3LzI1NSwxMjYvMjU1KSxyZ2IoMjM3LzI1NSwxMDUvMjU1LDM3LzI1NSkpKSgyMDApKQpgYGAKCmBgYHtyfQpNZWFzbGVzPC1yZWFkLmNzdigiTWVhc2xlc19pbW11bml6YXRpb24uY3N2IixzZXA9IjsiLGRlYz0iLiIpCmBgYAoKYGBge3J9Ck1lYXNsZXMkQ291bnRyeV8yPC1nc3ViKCJcXHMqXFwoW15cXCldK1xcKSIsIiIsYXMuY2hhcmFjdGVyKE1lYXNsZXMkQ291bnRyeSkpCmBgYAoKYGBge3J9Ck1lYXNsZXMkQ291bnRyeV8yW01lYXNsZXMkQ291bnRyeV8yPT0iVW5pdGVkIFN0YXRlcyJdPC0iVVMiCk1lYXNsZXMkQ291bnRyeV8yW01lYXNsZXMkQ291bnRyeV8yPT0iS29yZWEiXTwtIlNvdXRoIEtvcmVhIgpgYGAKCmBgYHtyfQpDT1ZJRF8zPC0gQ09WSURfMyAlPiUgaW5uZXJfam9pbihNZWFzbGVzLGJ5PWMoIkNvdW50cnkuUmVnaW9uIj0iQ291bnRyeV8yIikpICU+JSBzZWxlY3QoLWMoIkNvdW50cnkiKSkKYGBgCgoKIyMjIEhlYWx0aCBleHBlbmRpdHVyZSAoJSBvZiBHRFApCgpgYGB7cn0KSGVhbHRoX2V4cGVuZGl0dXJlPC1yZWFkLmNzdigiSGVhbHRoX2V4cGVuZGl0dXJlX0dEUC5jc3YiLHNlcD0iOyIsZGVjPSIuIikKYGBgCgpgYGB7cn0KSGVhbHRoX2V4cGVuZGl0dXJlJENvdW50cnlfMjwtZ3N1YigiXFxzKlxcKFteXFwpXStcXCkiLCIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihIZWFsdGhfZXhwZW5kaXR1cmUkQ291bnRyeSkpCmBgYAoKYGBge3J9CkhlYWx0aF9leHBlbmRpdHVyZSRDb3VudHJ5XzJbSGVhbHRoX2V4cGVuZGl0dXJlJENvdW50cnlfMj09IlVuaXRlZCBTdGF0ZXMiXTwtIlVTIgpIZWFsdGhfZXhwZW5kaXR1cmUkQ291bnRyeV8yW0hlYWx0aF9leHBlbmRpdHVyZSRDb3VudHJ5XzI9PSJLb3JlYSJdPC0iU291dGggS29yZWEiCmBgYAoKCmBgYHtyfQpDT1ZJRF8zPC0gQ09WSURfMyAlPiUgaW5uZXJfam9pbihIZWFsdGhfZXhwZW5kaXR1cmUsYnk9YygiQ291bnRyeS5SZWdpb24iPSJDb3VudHJ5XzIiKSkgJT4lIHNlbGVjdCgtYygiQ291bnRyeSIpKQpgYGAKCmBgYHtyfQpwbG90PC1nZ3Bsb3QoZGF0YT1DT1ZJRF8zLGFlcyh4PWxvZyhDYXNlc19taWxsaW9uKSx5PUV4cGVuZGl0dXJlXzIwMTYsCiAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9UmVjb3ZlcmVkX3BlcmNlbnRhZ2UsdGV4dD1Db3VudHJ5LlJlZ2lvbikpICsKICBnZW9tX3BvaW50KGNvbG9yPSJibGFjayIsZmlsbD1yZ2IoMjA0LzI1NSwxOTcvMjU1LDEyNi8yNTUpLHNoYXBlPTIxLGFscGhhPTAuNikgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDMsMTUpLCBuYW1lPSJSZWNvdmVyZWQgXG4gcGVyY2VudGFnZSIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsKICBsYWJzKHRpdGxlPSJIZWFsdGggZXhwZW5kaXR1cmUgKCUgb2YgR0RQKSBWcy4gbG9nYXJpdGhtdXMgb2YgQ09WSUQtMTkgY2FzZXMgYnkgbWlsbGlvbiBpbmhhYml0YW50cyBcbiBhbmQgcHJvcG9ydGlvbiBvZiByZWNvdmVyZWQiLAogICAgICAgeD0ibG4oQ2FzZXMvMU0gcG9wdWxhdGlvbikiLAogICAgICAgeT0iJSBvZiBHRFAgaW4gaGVhbHRoIikKCmdncGxvdGx5KHBsb3QsdG9vbHRpcCA9IGMoInRleHQiKSx3aWR0aD03NTApCmBgYAoKIyMjIFRlbXBlcmF0dXJlCgpgYGB7cn0KVGVtcGVyYXR1cmU8LXJlYWQuY3N2KCJHbG9iYWxMYW5kVGVtcGVyYXR1cmVzQnlDb3VudHJ5LmNzdiIsc2VwPSI7IikKYGBgCgpgYGB7cn0KRGF0ZV9UZW1wPC1hcy5EYXRlKFRlbXBlcmF0dXJlJGR0LCBmb3JtYXQ9IiVkLyVtLyVZIikgCgpUZW1wZXJhdHVyZSREYXRlX1RlbXA8LURhdGVfVGVtcApgYGAKCkV4dHJhY3QgbW9udGgsIGZpbHRlciBJUSAoSmFuLCBGZWIgYW5kIE1hcikgYW5kIG9idGFpbiB0aGUgYXZlcmFnZSBJUSB0ZW1wZXJhdHVyZToKYGBge3J9ClRlbXBlcmF0dXJlPC0gVGVtcGVyYXR1cmUgJT4lIG11dGF0ZShNb250aD1tb250aChUZW1wZXJhdHVyZSREYXRlX1RlbXAsbGFiZWwgPVRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWWVhcj15ZWFyKFRlbXBlcmF0dXJlJERhdGVfVGVtcCkpICU+JSAKICBmaWx0ZXIoTW9udGggJWluJSBjKCJKYW4iLCJGZWIiLCJNYXIiKSAmIFllYXI+PTIwMDApICAlPiUgCiAgZ3JvdXBfYnkoQ291bnRyeSkgJT4lIHN1bW1hcmlzZShBdmdfSVFfdGVtcGVyYXR1cmU9bWVhbihBdmVyYWdlVGVtcGVyYXR1cmUpKQoKVGVtcGVyYXR1cmU8LVRlbXBlcmF0dXJlWyFpcy5uYShUZW1wZXJhdHVyZSRBdmdfSVFfdGVtcGVyYXR1cmUpLF0KYGBgCgpgYGB7cn0KVGVtcGVyYXR1cmUkQ291bnRyeV8yPC1nc3ViKCJcXHMqXFwoW15cXCldK1xcKSIsIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKFRlbXBlcmF0dXJlJENvdW50cnkpKQpgYGAKCmBgYHtyfQpUZW1wZXJhdHVyZSRDb3VudHJ5XzJbVGVtcGVyYXR1cmUkQ291bnRyeV8yPT0iVW5pdGVkIFN0YXRlcyJdPC0iVVMiClRlbXBlcmF0dXJlJENvdW50cnlfMltUZW1wZXJhdHVyZSRDb3VudHJ5XzI9PSJLb3JlYSJdPC0iU291dGggS29yZWEiCmBgYAoKYGBge3J9CkNPVklEXzM8LSBDT1ZJRF8zICU+JSBpbm5lcl9qb2luKFRlbXBlcmF0dXJlLGJ5PWMoIkNvdW50cnkuUmVnaW9uIj0iQ291bnRyeV8yIikpICU+JSBzZWxlY3QoLWMoIkNvdW50cnkiKSkKCiN3cml0ZS5jc3YoQ09WSURfMywiQ09WSURfQ292YXJpYWJsZXMuY3N2IikKYGBgCgpgYGB7cn0KcGxvdDwtZ2dwbG90KGRhdGE9Q09WSURfMyxhZXMoeD1sb2coQ2FzZXNfbWlsbGlvbikseT1BdmdfSVFfdGVtcGVyYXR1cmUsCiAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9UmVjb3ZlcmVkX3BlcmNlbnRhZ2UsdGV4dD1Db3VudHJ5LlJlZ2lvbikpICsKICBnZW9tX3BvaW50KGNvbG9yPSJibGFjayIsZmlsbD1yZ2IoMTIwLzI1NSwyOC8yNTUsMTA5LzI1NSksc2hhcGU9MjEsYWxwaGE9MC42KSArCiAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMywxNSksIG5hbWU9IlJlY292ZXJlZCBcbiBwZXJjZW50YWdlIikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikgKwogIGxhYnModGl0bGU9IkF2ZXJhZ2UgSVEgdGVtcGVyYXR1cmUgVnMuIGxvZ2FyaXRobXVzIG9mIENPVklELTE5IGNhc2VzIGJ5IG1pbGxpb24gaW5oYWJpdGFudHMgXG4gYW5kIHByb3BvcnRpb24gb2YgcmVjb3ZlcmVkIiwKICAgICAgIHg9ImxuKENhc2VzLzFNIHBvcHVsYXRpb24pIiwKICAgICAgIHk9IlRlbXBlcmF0dXJlIChDZWxjaXVzKSIpCgpnZ3Bsb3RseShwbG90LHRvb2x0aXAgPSBjKCJ0ZXh0Iiksd2lkdGg9NzUwKQpgYGAKCiMjIEZvciB0aGUgbnVtYmVyIG9mIGRlYXRocwoKIyMjRFRQIGltbXVuaXphdGlvbgoKYGBge3J9CkRUUF9pbW11bml6YXRpb248LXJlYWQuY3N2KCJJbmZhbnRzX2xhY2tpbmdfaW1tdW5pemF0aW9uX0RUUC5jc3YiLHNlcD0iOyIpCmBgYAoKUmVtb3ZlIGFmdGVyIHBhcmVudGhlc2VzOgpgYGB7cn0KRFRQX2ltbXVuaXphdGlvbiRDb3VudHJ5XzI8LWdzdWIoIlxccypcXChbXlxcKV0rXFwpIiwiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKERUUF9pbW11bml6YXRpb24kQ291bnRyeSkpCmBgYAoKCmBgYHtyfQpEVFBfaW1tdW5pemF0aW9uJENvdW50cnlfMltEVFBfaW1tdW5pemF0aW9uJENvdW50cnlfMj09IlVuaXRlZCBTdGF0ZXMiXTwtIlVTIgpEVFBfaW1tdW5pemF0aW9uJENvdW50cnlfMltEVFBfaW1tdW5pemF0aW9uJENvdW50cnlfMj09IktvcmVhIl08LSJTb3V0aCBLb3JlYSIKYGBgCgoKYGBge3Isd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQpDT1ZJRF80PC0gQ09WSURfQ291bnRyeSAlPiUgaW5uZXJfam9pbihEVFBfaW1tdW5pemF0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieT1jKCJDb3VudHJ5LlJlZ2lvbiI9IkNvdW50cnlfMiIpKSAlPiUgCiAgc2VsZWN0KENvdW50cnkuUmVnaW9uLFRvdGFsX2NvbmZpcm1lZCxUb3RhbF9kZWF0aHMsVG90YWxfUmVjb3ZlcmVkLAogICAgICAgICBMYWNrX0RUUF9pbm1tdW5pemF0aW9uXzIwMTgpICU+JSAKICBtdXRhdGUoUmVjb3ZlcmVkX3BlcmNlbnRhZ2U9KFRvdGFsX1JlY292ZXJlZC9Ub3RhbF9jb25maXJtZWQpKjEwMCwKICAgICAgICAgRGVhdGhfcmF0ZT0oVG90YWxfZGVhdGhzL1RvdGFsX2NvbmZpcm1lZCkqMTAwKQpgYGAKCmBgYHtyfQpwbG90PC1nZ3Bsb3QoZGF0YT1DT1ZJRF80LGFlcyh4PURlYXRoX3JhdGUseT1MYWNrX0RUUF9pbm1tdW5pemF0aW9uXzIwMTgsCiAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9UmVjb3ZlcmVkX3BlcmNlbnRhZ2UsdGV4dD1Db3VudHJ5LlJlZ2lvbikpICsKICBnZW9tX3BvaW50KGNvbG9yPSJibGFjayIsZmlsbD1yZ2IoMTIwLzI1NSwyOC8yNTUsMTA5LzI1NSksc2hhcGU9MjEsYWxwaGE9MC42KSArCiAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMywxNSksIG5hbWU9IlJlY292ZXJlZCBcbiBwZXJjZW50YWdlIikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikgKwogIGxhYnModGl0bGU9IiUgaW5mYW50cyBsYWNraW5nIERUUCBpbW11bml6YXRpb24gVnMuIGRlYXRoIHJhdGUgYW5kIFxuIHByb3BvcnRpb24gb2YgcmVjb3ZlcmVkIiwKICAgICAgIHg9Ik5vdmVsIGNvcm9uYXZpcnVzIGRlYXRoIHJhdGUiLAogICAgICAgeT0iJSBvZiBpbmZhbnRzIikKCmdncGxvdGx5KHBsb3QsdG9vbHRpcCA9IGMoInRleHQiKSx3aWR0aD03NTApCmBgYAoKIyMjIEluZmFudHMgbGFja2luZyBpbW11bml6YXRpb24sIG1lYXNsZXMgKCUgb2Ygb25lLXllYXItb2xkcykKCmBgYHtyfQpDT1ZJRF80PC0gQ09WSURfNCAlPiUgaW5uZXJfam9pbihNZWFzbGVzLGJ5PWMoIkNvdW50cnkuUmVnaW9uIj0iQ291bnRyeV8yIikpICU+JSBzZWxlY3QoLWMoIkNvdW50cnkiKSkKYGBgCgoKYGBge3J9CmdncGxvdChDT1ZJRF80LCBhZXMoeT1NZWFzbGVzXzIwMTgpKSArIAogIGdlb21fYm94cGxvdChmaWxsPSJkb2RnZXJibHVlNCIsb3V0bGllci5zaGFwZSA9IDIxLCAKICAgICAgICAgICAgICAgb3V0bGllci5maWxsID0gImZpcmVicmljayIsYWxwaGE9MC43NSkgKwogIGdndGl0bGUoIkJveHBsb3Qgb2YgJSBpbmZhbnRzIGxhY2tpbmcgbWVhc2xlcyBpbW11bml6YXRpb24iKSArIHlsYWIoIiUgb2YgaW5mYW50cyIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpgYGB7cn0KZ2dwbG90KENPVklEXzQsIGFlcyhNZWFzbGVzXzIwMTgpKSArIAogIGdlb21faGlzdG9ncmFtKGZpbGw9ImRvZGdlcmJsdWU0IixiaW5zPTIwLGFscGhhPTAuOCkgKwogIGdndGl0bGUoIkhpc3RvZ3JhbSBvZiAlIGluZmFudHMgbGFja2luZyBtZWFzbGVzIGltbXVuaXphdGlvbiIpICsgCiAgeGxhYigiJSBvZiBpbmZhbnRzIikgKyAKICB5bGFiKCJDb3VudCIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpgYGB7cn0KcGxvdDwtZ2dwbG90KGRhdGE9Q09WSURfNCxhZXMoeD1EZWF0aF9yYXRlLHk9TWVhc2xlc18yMDE4LAogICAgICAgICAgICAgICAgICAgICAgICBzaXplPVJlY292ZXJlZF9wZXJjZW50YWdlLHRleHQ9Q291bnRyeS5SZWdpb24pKSArCiAgZ2VvbV9wb2ludChjb2xvcj0iYmxhY2siLGZpbGw9cmdiKDIzNy8yNTUsMTA1LzI1NSwzNy8yNTUpLHNoYXBlPTIxLGFscGhhPTAuNikgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDMsMTUpLCBuYW1lPSJSZWNvdmVyZWQgXG4gcGVyY2VudGFnZSIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsKICBsYWJzKHRpdGxlPSIlIGluZmFudHMgbGFja2luZyBtZWFzbGVzIGltbXVuaXphdGlvbiBWcy4gZmF0YWxpdHkgcmF0ZSBcbiBhbmQgcHJvcG9ydGlvbiBvZiByZWNvdmVyZWQiLAogICAgICAgeD0iRmF0YWxpdHkgcmF0ZSAoJSkiLAogICAgICAgeT0iJSBvZiBpbmZhbnRzIikKCmdncGxvdGx5KHBsb3QsdG9vbHRpcCA9IGMoInRleHQiKSx3aWR0aD03NTApCmBgYAoKCiMgRml0dGluZyBhIHJlZ3Jlc3Npb24gbW9kZWwKClRyYW5zZm9ybWluZyB3aXRoIGxuIGFuZCBjb252ZXJ0aW5nIHRlbXBlcmF0dXJlIGFzIGtlbHZpbjoKYGBge3J9Ck1vZDE8LWxtKGxvZyhDT1ZJRF8zJENhc2VzX21pbGxpb24pfmxvZyhDT1ZJRF8zJFllYXJfMjAxOCkrbG9nKENPVklEXzMkTWVhc2xlc18yMDE4KSsKICAgICAgICAgICBsb2coQ09WSURfMyRFeHBlbmRpdHVyZV8yMDE2KStsb2coQ09WSURfMyRBdmdfSVFfdGVtcGVyYXR1cmUrMjczLjE1KSkKc3VtbWFyeShNb2QxKQpgYGAKClN0ZXB3aXNlIHdpdGggQUlDIGNyaXRlcnRpb246CmBgYHtyfQpNb2QyPC1zdGVwKE1vZDEsZGlyZWN0aW9uID0gImJvdGgiKQpgYGAKCmBgYHtyfQpzdW1tYXJ5KE1vZDIpCmBgYAoKTm9ybWFsaXR5IG9mIHJlc2lkdWFsczoKYGBge3J9Cmhpc3QoTW9kMiRyZXNpZHVhbHMpCnNoYXBpcm8udGVzdChNb2QyJHJlc2lkdWFscykKYGBgCgpQcmVkaWN0aW9uIHBvd2VyOiBzZXBhcmF0ZSBiZXR3ZWVuIHRyYWluaW5nIGFuZCB0ZXN0aW5nOgpgYGB7cn0Kc2V0LnNlZWQoMTc5ODE5KQpTYW1wbGUgPC0gc2FtcGxlKDE6bGVuZ3RoKENPVklEXzMkQ2FzZXNfbWlsbGlvbiksbGVuZ3RoKENPVklEXzMkQ2FzZXNfbWlsbGlvbikqMC4yMCkKdC50ZXN0aW5nPC0gQ09WSURfM1tTYW1wbGUsXQp0LnRyYWluaW5nPC0gQ09WSURfM1stU2FtcGxlLF0KYGBgCgpUcmFuc2Zvcm0gdGhlIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHZhcmlhYmxlcyBhcyBiZWZvcmU6CmBgYHtyfQp0LnRyYWluaW5nPC10LnRyYWluaW5nICU+JSBtdXRhdGUoQ2FzZXNfbWlsbGlvbl9sb2c9bG9nKENhc2VzX21pbGxpb24pLEhESV9sb2c9bG9nKFllYXJfMjAxOCksCiAgICAgICAgICAgICAgICAgICAgICBHRFBfbG9nPWxvZyhFeHBlbmRpdHVyZV8yMDE2KSwKICAgICAgICAgICAgICAgICAgICAgIFRlbXBlcmF0dXJlX2xvZ19rZWx2aW49bG9nKEF2Z19JUV90ZW1wZXJhdHVyZSsyNzMuMTUpKSAKCnQudHJhaW5pbmc8LXQudHJhaW5pbmdbLDE0OjE3XQoKdC50ZXN0aW5nPC10LnRlc3RpbmcgJT4lIG11dGF0ZShDYXNlc19taWxsaW9uX2xvZz1sb2coQ2FzZXNfbWlsbGlvbiksSERJX2xvZz1sb2coWWVhcl8yMDE4KSwKICAgICAgICAgICAgICAgICAgICAgIEdEUF9sb2c9bG9nKEV4cGVuZGl0dXJlXzIwMTYpLAogICAgICAgICAgICAgICAgICAgICAgVGVtcGVyYXR1cmVfbG9nX2tlbHZpbj1sb2coQXZnX0lRX3RlbXBlcmF0dXJlKzI3My4xNSkpIAoKdC50ZXN0aW5nPC10LnRlc3RpbmdbLDE0OjE3XQpgYGAKCkZpdCB0aGUgc2FtZSBtb2RlbCB3aXRoIHRyYWluaW5nCmBgYHtyfQpNb2QzPC1sbShDYXNlc19taWxsaW9uX2xvZ34uLCBkYXRhPXQudHJhaW5pbmcpCnN1bW1hcnkoTW9kMykKYGBgCgpFcnJvciBmdW5jdGlvbnM6CmBgYHtyfQojIFJlc2lkdWFsIFN1bSBvZiBTcXVhcmUgKFJTUykKUlNTPC1mdW5jdGlvbihQcmVkLEFjdHVhbCkgewogIHNzPC1zdW0oKEFjdHVhbC1QcmVkKV4yKQogIHJldHVybihzcykKfQoKIyBSZXNpZHVhbCBTdGFuZGFyZCBFcnJvciAoUlNFKQpSU0U8LWZ1bmN0aW9uKFByZWQsUmVhbCxOdW1QcmVkKSB7CiAgTjwtbGVuZ3RoKFJlYWwpLU51bVByZWQtMSAgCiAgc3M8LXNxcnQoKDEvTikqUlNTKFByZWQsUmVhbCkpCiAgcmV0dXJuKHNzKQp9CiMgTWVhbiBTcXVhcmVkIEVycm9yIApNU0UgPC0gZnVuY3Rpb24oUHJlZCxBY3R1YWwpIHsKICBOPC1sZW5ndGgoQWN0dWFsKQogIHNzPC0oMS9OKSpSU1MoUHJlZCxBY3R1YWwpCiAgcmV0dXJuKHNzKQp9CgojIFJlbGF0aXZlIGVycm9yClJlbGF0aXZlRXJyb3I8LWZ1bmN0aW9uKFByZWQsQWN0dWFsKSB7CiAgc3M8LXN1bShhYnMoQWN0dWFsLVByZWQpKS9zdW0oYWJzKEFjdHVhbCkpCiAgcmV0dXJuKHNzKQp9CmBgYAoKUHJlZGljdGlvbjoKYGBge3J9ClByZWQ8LXByZWRpY3QoTW9kMyx0LnRlc3RpbmcpCmBgYAoKRXJyb3JzOgpgYGB7cn0KUlNTX01vZDM8LVJTUyhQcmVkLHQudGVzdGluZyRDYXNlc19taWxsaW9uX2xvZykKUlNFX01vZDM8LVJTRShQcmVkLHQudGVzdGluZyRDYXNlc19taWxsaW9uX2xvZyxkaW0odC50ZXN0aW5nKVsyXS0xKQpNU0VfTW9kMzwtTVNFKFByZWQsdC50ZXN0aW5nJENhc2VzX21pbGxpb25fbG9nKQpSZWxhdGl2ZUVycm9yX01vZDM8LVJlbGF0aXZlRXJyb3IoUHJlZCx0LnRlc3RpbmckQ2FzZXNfbWlsbGlvbl9sb2cpCgpNb2QzX0Vycm9yczwtYyhSU1NfTW9kMyxSU0VfTW9kMyxNU0VfTW9kMyxSZWxhdGl2ZUVycm9yX01vZDMpCmBgYAoKTm93LCBhIG1vZGVsIHdpdGhvdXQgdGVtcGVyYXR1cmU6CmBgYHtyfQp0LnRyYWluaW5nIDwtIHQudHJhaW5pbmcgJT4lIHNlbGVjdCgtVGVtcGVyYXR1cmVfbG9nX2tlbHZpbikKdC50ZXN0aW5nIDwtIHQudGVzdGluZyAlPiUgc2VsZWN0KC1UZW1wZXJhdHVyZV9sb2dfa2VsdmluKQpgYGAKCmBgYHtyfQpNb2Q0PC1sbShDYXNlc19taWxsaW9uX2xvZ34uLCBkYXRhPXQudHJhaW5pbmcpCnN1bW1hcnkoTW9kNCkKYGBgCgpQcmVkaWN0aW9uOgpgYGB7cn0KUHJlZDwtcHJlZGljdChNb2Q0LHQudGVzdGluZykKYGBgCgpFcnJvcnM6CmBgYHtyfQpSU1NfTW9kNDwtUlNTKFByZWQsdC50ZXN0aW5nJENhc2VzX21pbGxpb25fbG9nKQpSU0VfTW9kNDwtUlNFKFByZWQsdC50ZXN0aW5nJENhc2VzX21pbGxpb25fbG9nLGRpbSh0LnRlc3RpbmcpWzJdLTEpCk1TRV9Nb2Q0PC1NU0UoUHJlZCx0LnRlc3RpbmckQ2FzZXNfbWlsbGlvbl9sb2cpClJlbGF0aXZlRXJyb3JfTW9kNDwtUmVsYXRpdmVFcnJvcihQcmVkLHQudGVzdGluZyRDYXNlc19taWxsaW9uX2xvZykKCk1vZDRfRXJyb3JzPC1jKFJTU19Nb2Q0LFJTRV9Nb2Q0LE1TRV9Nb2Q0LFJlbGF0aXZlRXJyb3JfTW9kNCkKYGBgCgpDcmVhdGUgYSByYWRhcnBsb3Q6CmBgYHtyfQpFcnJvcnM8LXJiaW5kKE1vZDNfRXJyb3JzLE1vZDRfRXJyb3JzKQoKcm93bmFtZXMoRXJyb3JzKTwtYygiTW9kZWwgd2l0aCB0ZW1wZXJhdHVyZSIsIk1vZGVsIHdpdGhvdXQgdGVtcGVyYXR1cmUiKQoKY29sbmFtZXMoRXJyb3JzKTwtYygiUmVzaWR1YWwgU3VtIG9mIFNxdWFyZSIsIlJlc2lkdWFsIFN0YW5kYXJkIEVycm9yIiwiTWVhbiBTcXVhcmVkIEVycm9yIiwiUmVsYXRpdmUgZXJyb3IiKQoKRXJyb3JzPC1hcy5kYXRhLmZyYW1lKEVycm9ycykKCm1heGltdW08LWFwcGx5KEVycm9ycywyLG1heCkKCm1pbmltdW08LWFwcGx5KEVycm9ycywyLG1pbikKCkVycm9yczwtcmJpbmQobWluaW11bSxFcnJvcnMpCgpFcnJvcnM8LXJiaW5kKG1heGltdW0sRXJyb3JzKQpgYGAKCmBgYHtyfQpyYWRhcmNoYXJ0KEVycm9ycyxtYXhtaW49VFJVRSxheGlzdHlwZT00LGF4aXNsYWJjb2w9InNsYXRlZ3JheTQiLAogICAgICAgICAgIGNlbnRlcnplcm89RkFMU0Usc2VnPTgsY2dsY29sPSJncmF5NjciLAogICAgICAgICAgIHBjb2w9YygiZG9kZ2VyYmx1ZTIiLCJmaXJlYnJpY2syIiwiZGFya29yYW5nZTIiLCJkYXJrb3JjaGlkMiIpLAogICAgICAgICAgIHBsdHk9MSwKICAgICAgICAgICBwbHdkPTMsCiAgICAgICAgICAgdGl0bGU9IkVycm9yIGNvbXBhcmlzb24iKQoKbGVnZW5kIDwtbGVnZW5kKDEuNSwxLCBsZWdlbmQ9YygiV2l0aCB0ZW1wZXJhdHVyZSIsIldpdGhvdXQgdGVtcGVyYXR1cmUiKSwKICAgICAgICAgICAgICAgICBzZWcubGVuPS0xLjQsCiAgICAgICAgICAgICAgICAgdGl0bGU9IkVycm9ycyIsCiAgICAgICAgICAgICAgICAgcGNoPTIxLCAKICAgICAgICAgICAgICAgICBidHk9Im4iICxsd2Q9MywgeS5pbnRlcnNwPTEsIGhvcml6PUZBTFNFLAogICAgICAgICAgICAgICAgIGNvbD1jKCJkb2RnZXJibHVlMiIsImZpcmVicmljazIiLCJkYXJrb3JhbmdlMiIsImRhcmtvcmNoaWQyIikpCmBgYAoKIyBGb3JlY2FzdCBieSBjb3VudHJ5CgojIyBSZXB1YmxpYyBvZiBDb3N0YSBSaWNhCgohW10oQ29zdGFSaWNhRmxhZy5wbmcpe3dpZHRoPTQwJX0KCipNb2RlbCB3aXRoIHRlbXBlcmF0dXJlKgoKYGBge3J9CkNvc3RhUmljYV9UZW1wPC1yZWFkLmNzdigiQ29zdGFSaWNhX1RlbXBlcmF0dXJlLmNzdiIsc2VwPSI7IikKQ29zdGFSaWNhX1RlbXAkRGF0ZTwtYXMuRGF0ZShDb3N0YVJpY2FfVGVtcCREYXRlLGZvcm1hdD0iJWQvJW0vJVkiKQpgYGAKCmBgYHtyfQpDT1ZJRF8yX0RheV9Db3N0YVJpY2FfVGVtcDwtQ09WSURfMl9EYXlfQ29zdGFSaWNhICU+JQogIGlubmVyX2pvaW4oQ29zdGFSaWNhX1RlbXAsYnk9YygiRGF0ZTIiPSJEYXRlIikpCgpDT1ZJRF9EYXlfc2VyaWVzX0Nvc3RhUmljYV9UZW1wPC14dHMoQ09WSURfMl9EYXlfQ29zdGFSaWNhX1RlbXAkV29ybGRfY29uZmlybWVkLCBvcmRlci5ieT1DT1ZJRF8yX0RheV9Db3N0YVJpY2FfVGVtcCREYXRlMikKYGBgCgpgYGB7cn0KQ09WSURfc2VyaWVzX0Nvc3RhUmljYV9UZW1wX1RyYWluPC1DT1ZJRF9EYXlfc2VyaWVzX0Nvc3RhUmljYV9UZW1wWzE6NjRdICNVbnRpbCBNYXJjaCAyNXRoCkNPVklEX3Nlcmllc19Db3N0YVJpY2FfVGVtcF9UZXN0PC1DT1ZJRF9EYXlfc2VyaWVzX0Nvc3RhUmljYV9UZW1wWzY1Omxlbmd0aChDT1ZJRF9EYXlfc2VyaWVzX0Nvc3RhUmljYV9UZW1wKV0gI0Zyb20gTWFyY2ggMjZ0aCBvbndhcmRzCmBgYAoKR2V0IHRoZSBsYWdnZWQgZGlmZmVyZW5jZToKYGBge3J9CnBsb3QoZGlmZihDT1ZJRF9zZXJpZXNfQ29zdGFSaWNhX1RlbXBfVHJhaW4pLHR5cGU9ImwiLG1haW49IjFzdCAiKSAKYGBgCgpDb3JyZWxvZ3JhbXMgb2YgZmlyc3QgZGlmZXJlbmNlOgpgYGB7cn0KdHNkaXNwbGF5KGRpZmYoQ09WSURfc2VyaWVzX0Nvc3RhUmljYV9UZW1wX1RyYWluKSkKYGBgCgpBcmltYSBtb2RlbDogQVJJTUEoMSwyLDEpCmBgYHtyfQojQXV0byBBcmltYSBmb3IgQ29zdGEgUmljYToKCkFSSU1BMV9Db3N0YVJpY2E8LWF1dG8uYXJpbWEoQ09WSURfc2VyaWVzX0Nvc3RhUmljYV9UZW1wX1RyYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhyZWcgPSBDT1ZJRF8yX0RheV9Db3N0YVJpY2FfVGVtcCRUZW1wZXJhdHVyZV9Db3N0YVJpY2FbMTo2NF0pCnN1bW1hcnkoQVJJTUExX0Nvc3RhUmljYSkKYGBgCgpQcmVkaWN0aXZlIGVycm9yIGZ1bmN0aW9uczoKYGBge3J9CiNSZWxhdGl2ZSBlcnJvcgpSRSA8LSBmdW5jdGlvbihGb3JlLEFjdHVhbCkgewogIHJldHVybihzdW0oYWJzKEZvcmUtQWN0dWFsKSkvYWJzKHN1bShBY3R1YWwpKSkKfQoKCiNNQVBFCk1BUEU8LWZ1bmN0aW9uKEZvcmUsQWN0dWFsKXsKICByZXR1cm4oCiAgICBtZWFuKGFicyhBY3R1YWwtRm9yZSkvYWJzKEFjdHVhbCkpKjEwMAogICAgKQp9CgojIG1lYW4gc3F1YXJlZCBlcnJvciAoTVNFKQpNU0U8LWZ1bmN0aW9uKEZvcmUsQWN0dWFsKSB7CiAgTjwtbGVuZ3RoKEFjdHVhbCkKICBzczwtc3VtKChBY3R1YWwtRm9yZSleMikKICByZXR1cm4oKDEvTikqc3MpCn0KCiNQRkEKUEZBIDwtIGZ1bmN0aW9uKEZvcmUsQWN0dWFsKSB7CiAgVG90YWw8LTAKICBOPC1sZW5ndGgoRm9yZSkKICBmb3IoaSBpbiAxOk4pIHsKICAgIGlmKEZvcmVbaV0+PUFjdHVhbFtpXSkKICAgICAgVG90YWw8LVRvdGFsKzEgICAgICAKICB9CiAgcmV0dXJuKFRvdGFsL04pCn0KYGBgCgpGb3JlY2FzdCBwcmVkaWN0aW9uIHRvIGNvbXBhcmUKYGBge3J9ClRlc3RfQ29zdGFSaWNhX1RlbXA8LUNPVklEXzJfRGF5X0Nvc3RhUmljYV9UZW1wJFRlbXBlcmF0dXJlX0Nvc3RhUmljYVs2NTpsZW5ndGgoQ09WSURfRGF5X3Nlcmllc19Db3N0YVJpY2FfVGVtcCldCgoKUF9DUklfMTwtZm9yZWNhc3QoQVJJTUExX0Nvc3RhUmljYSx4cmVnID0gVGVzdF9Db3N0YVJpY2FfVGVtcCkKYGBgCgpDYWxjdWxhdGUgZXJyb3JzOgpgYGB7cn0KUkVfQ1JJXzE8LVJFKFBfQ1JJXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0Nvc3RhUmljYV9UZW1wX1Rlc3QpKQpNQVBFX0NSSV8xPC1NQVBFKFBfQ1JJXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0Nvc3RhUmljYV9UZW1wX1Rlc3QpKQpNU0VfQ1JJXzE8LU1TRShQX0NSSV8xJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19Db3N0YVJpY2FfVGVtcF9UZXN0KSkKUEZBX0NSSV8xPC1QRkEoUF9DUklfMSRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfQ29zdGFSaWNhX1RlbXBfVGVzdCkpCgpFcnJvcnMuQ1JJXzE8LWMoUkVfQ1JJXzEsTUFQRV9DUklfMSxNU0VfQ1JJXzEsUEZBX0NSSV8xKQpFcnJvcnMuQ1JJXzEKYGBgCgpNYW51YWwgTUFQRToKYGBge3J9CmRkPC1kYXRhLmZyYW1lKEFjdHVhbD1hcy52ZWN0b3IoQ09WSURfc2VyaWVzX0Nvc3RhUmljYV9UZW1wX1Rlc3QpLAogICAgICAgICAgICAgICBGb3JlPVBfQ1JJXzEkbWVhbgopCmRkPC1kZCAlPiUgbXV0YXRlKEFic19QZXJfRXJyb3I9YWJzKEFjdHVhbC1Gb3JlKS9hYnMoQWN0dWFsKSkKbWVhbihkZCRBYnNfUGVyX0Vycm9yKSoxMDAKYGBgCgoqTW9kZWwgd2l0aG91dCB0ZW1wZXJhdHVyZSoKCkFSSU1BKDEsMiwwKQpgYGB7cn0KI0F1dG8gQXJpbWEgZm9yIENvc3RhIFJpY2E6CgpBUklNQTJfQ29zdGFSaWNhPC1hdXRvLmFyaW1hKENPVklEX3Nlcmllc19Db3N0YVJpY2FfVGVtcF9UcmFpbikKc3VtbWFyeShBUklNQTJfQ29zdGFSaWNhKQpgYGAKCkZvcmVjYXN0IHByZWRpY3Rpb24gdG8gY29tcGFyZQpgYGB7cn0KUF9DUklfMjwtZm9yZWNhc3QoQVJJTUEyX0Nvc3RhUmljYSxoPWxlbmd0aChDT1ZJRF9zZXJpZXNfQ29zdGFSaWNhX1RlbXBfVGVzdCkpCmBgYAoKQ2FsY3VsYXRlIGVycm9yczoKYGBge3J9ClJFX0NSSV8yPC1SRShQX0NSSV8yJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19Db3N0YVJpY2FfVGVtcF9UZXN0KSkKTUFQRV9DUklfMjwtTUFQRShQX0NSSV8yJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19Db3N0YVJpY2FfVGVtcF9UZXN0KSkKTVNFX0NSSV8yPC1NU0UoUF9DUklfMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfQ29zdGFSaWNhX1RlbXBfVGVzdCkpClBGQV9DUklfMjwtUEZBKFBfQ1JJXzIkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0Nvc3RhUmljYV9UZW1wX1Rlc3QpKQoKRXJyb3JzLkNSSV8yPC1jKFJFX0NSSV8yLE1BUEVfQ1JJXzIsTVNFX0NSSV8yLFBGQV9DUklfMikKRXJyb3JzLkNSSV8yCmBgYAoKYGBge3J9CkVycm9yczwtcmJpbmQoRXJyb3JzLkNSSV8xLEVycm9ycy5DUklfMikKCnJvd25hbWVzKEVycm9ycyk8LWMoIkFSSU1BWCgxLDIsMSkiLCJBUklNQSgxLDIsMCkiKQoKY29sbmFtZXMoRXJyb3JzKTwtYygiUmVsYXRpdmUgZXJyb3IiLCJNZWFuIEFicyAlIGVycm9yIiwiTWVhbiBTcXVhcmVkIEVycm9yIiwiUEZBIikKCkVycm9yczwtYXMuZGF0YS5mcmFtZShFcnJvcnMpCgptYXhpbXVtPC1hcHBseShFcnJvcnMsMixtYXgpCgptaW5pbXVtPC1hcHBseShFcnJvcnMsMixtaW4pCgpFcnJvcnM8LXJiaW5kKG1pbmltdW0sRXJyb3JzKQoKRXJyb3JzPC1yYmluZChtYXhpbXVtLEVycm9ycykKYGBgCgpgYGB7cn0KcmFkYXJjaGFydChFcnJvcnMsbWF4bWluPVRSVUUsYXhpc3R5cGU9NCxheGlzbGFiY29sPSJzbGF0ZWdyYXk0IiwKICAgICAgICAgICBjZW50ZXJ6ZXJvPUZBTFNFLHNlZz04LGNnbGNvbD0iZ3JheTY3IiwKICAgICAgICAgICBwY29sPWMoImRvZGdlcmJsdWUyIiwiZmlyZWJyaWNrMiIsImRhcmtvcmFuZ2UyIiwiZGFya29yY2hpZDIiKSwKICAgICAgICAgICBwbHR5PTEsCiAgICAgICAgICAgcGx3ZD0zLAogICAgICAgICAgIHRpdGxlPSJFcnJvciBjb21wYXJpc29uIikKCmxlZ2VuZCA8LWxlZ2VuZCgxLjUsMSwgbGVnZW5kPWMoIkFSSU1BWCgxLDIsMSkiLCJBUklNQSgxLDIsMCkiKSwKICAgICAgICAgICAgICAgICBzZWcubGVuPS0xLjQsCiAgICAgICAgICAgICAgICAgdGl0bGU9IkVycm9ycyIsCiAgICAgICAgICAgICAgICAgcGNoPTIxLCAKICAgICAgICAgICAgICAgICBidHk9Im4iICxsd2Q9MywgeS5pbnRlcnNwPTEsIGhvcml6PUZBTFNFLAogICAgICAgICAgICAgICAgIGNvbD1jKCJkb2RnZXJibHVlMiIsImZpcmVicmljazIiLCJkYXJrb3JhbmdlMiIsImRhcmtvcmNoaWQyIikpCmBgYAoKQ29uY2x1c2lvbjogS2VlcCBtb2RlbCB3aXRoIHRlbXBlcmF0dXJlCgpNYWtlIG1vZGVsIHdpdGggdGhlIG92ZXJhbGwgc2VyaWVzCmBgYHtyfQojQXV0byBBcmltYSBmb3IgQ29zdGEgUmljYToKCkFSSU1BX0ZpbmFsX0Nvc3RhUmljYTwtYXV0by5hcmltYShDT1ZJRF9EYXlfc2VyaWVzX0Nvc3RhUmljYV9UZW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhyZWcgPSBDT1ZJRF8yX0RheV9Db3N0YVJpY2FfVGVtcCRUZW1wZXJhdHVyZV9Db3N0YVJpY2EpCnN1bW1hcnkoQVJJTUFfRmluYWxfQ29zdGFSaWNhKQpgYGAKCkZpbmFsIGZvcmVjYXN0OgpgYGB7cn0KRnV0dXJlX0Nvc3RhUmljYV9UZW1wPC1Db3N0YVJpY2FfVGVtcCRUZW1wZXJhdHVyZV9Db3N0YVJpY2FbQ29zdGFSaWNhX1RlbXAkRGF0ZT5tYXgoQ09WSURfMiREYXRlMildCgpQX0NSSV9GaW5hbDwtZm9yZWNhc3QoQVJJTUFfRmluYWxfQ29zdGFSaWNhLHhyZWcgPSBGdXR1cmVfQ29zdGFSaWNhX1RlbXApCkxvd19saW1fQ1JJPC1kYXRhLmZyYW1lKFBfQ1JJX0ZpbmFsJGxvd2VyKVssMl0KVXBwX2xpbV9DUkk8LWRhdGEuZnJhbWUoUF9DUklfRmluYWwkdXBwZXIpWywyXQpgYGAKCkZvciBtYWtpbmcgdGhlIHBsb3Q6CmBgYHtyfQojI0RhdGEgcGVyaW9kcwpwZXJfMSA8LSBhcy5EYXRlKGFzLmNoYXJhY3RlcihDT1ZJRF8yX0RheV9Db3N0YVJpY2FfVGVtcCREYXRlMikpCnBlcl8yIDwtIHNlcShhcy5EYXRlKG1heChDT1ZJRF8yX0RheV9Db3N0YVJpY2FfVGVtcCREYXRlMikrMSxmb3JtYXQ9IiVZLSVtLSVkIiksIGFzLkRhdGUoIjIwMjAtMDQtMzAiLGZvcm1hdD0iJVktJW0tJWQiKSwiMSBkYXkiKQoKCiMgTWVyZ2UgZm9yZWNhc3QgKyBhY3R1YWxzCmRhdGEgPC0geHRzKENPVklEX0RheV9zZXJpZXNfQ29zdGFSaWNhX1RlbXAsb3JkZXIuYnk9cGVyXzEpIApkYXRhTkEgPC0gcmVwKE5BLCBsZW5ndGgoZGF0YSkpCkEgPC0gY2JpbmQoZGF0YSxkYXRhTkEsZGF0YU5BLGRhdGFOQSkKCgpMb3dfbGltX0NSSSA8LSB4dHMoTG93X2xpbV9DUkksb3JkZXIuYnk9cGVyXzIpCkZvcmVjYXN0X0NSSSA8LSB4dHMoUF9DUklfRmluYWwkbWVhbixvcmRlci5ieT1wZXJfMikKVXBwX2xpbV9DUkkgPC0geHRzKFVwcF9saW1fQ1JJLG9yZGVyLmJ5PXBlcl8yKQpwcmVkTkEgPC0gcmVwKE5BLCBsZW5ndGgoRm9yZWNhc3RfQ1JJKSkKQiA8LSBjYmluZChwcmVkTkEsIExvd19saW1fQ1JJLCBGb3JlY2FzdF9DUkksIFVwcF9saW1fQ1JJKQoKYWxsX3Nlcmllc19DUkkgPC0gZGF0YS5mcmFtZShyYmluZChhcy5tYXRyaXgoQSksYXMubWF0cml4KEIpKSkKY29sbmFtZXMoYWxsX3Nlcmllc19DUkkpIDwtIGMoJ0FjdHVhbCcsICdMb3dlcl9saW1pdCcsICdGb3JlY2FzdCcsICdVcHBlcl9saW1pdCcpCmBgYAoKYGBge3J9CmR5Z3JhcGgoYWxsX3Nlcmllc19DUkksIG1haW49IlNBUlMtQ09WMi1vdXRicmVhazogVG90YWwgQ29zdGEgUmljYSBjYXNlcyIseGxhYj0iRGF0ZSIsIHlsYWI9Ik5vdmVsIGNvcm9uYXZpcnVzIGNhc2VzIix3aWR0aCA9IDc1MCklPiUKICBkeVNlcmllcyhjKCdMb3dlcl9saW1pdCcsICdGb3JlY2FzdCcsICdVcHBlcl9saW1pdCcpLGxhYmVsPSJGb3JlY2FzdCIsc3Ryb2tlV2lkdGg9MiwKICAgICAgICAgICBkcmF3UG9pbnRzID0gVFJVRSwgcG9pbnRTaXplID0gMiwgY29sb3I9cmdiKDE4OS8yNTUsNDQvMjU1LDQ3LzI1NSkpICU+JQogIGR5U2VyaWVzKCJBY3R1YWwiLGRyYXdQb2ludHMgPSBUUlVFLCBzdHJva2VXaWR0aD0yLCBwb2ludFNpemUgPSAyLAogICAgICAgICAgIGNvbG9yPXJnYigxMC8yNTUsNDQvMjU1LDExOS8yNTUpKSAlPiUgCiAgZHlSYW5nZVNlbGVjdG9yKCkKCmBgYAoKIyMgTWV4aWNvCgohW10oTWV4aWNvRmxhZy5wbmcpe3dpZHRoPTQwJX0KCipNb2RlbCB3aXRoIHRlbXBlcmF0dXJlKgoKYGBge3J9Ck1leGljb19UZW1wPC1yZWFkLmNzdigiTWV4aWNvX1RlbXBlcmF0dXJlLmNzdiIsc2VwPSI7IikKTWV4aWNvX1RlbXAkRGF0ZTwtYXMuRGF0ZShNZXhpY29fVGVtcCREYXRlLGZvcm1hdD0iJWQvJW0vJVkiKQpgYGAKCmBgYHtyfQpDT1ZJRF8yX0RheV9NZXhpY288LSBDT1ZJRF8yICU+JSAKICBmaWx0ZXIoQ291bnRyeS5SZWdpb24gJWluJSBjKCJNZXhpY28iKSkgJT4lIAogIGdyb3VwX2J5KERhdGUyKSAlPiUgc3VtbWFyaXNlKFdvcmxkX2NvbmZpcm1lZD1zdW0oQ29uZmlybWVkKSkKCkNPVklEXzJfRGF5X01leGljb19UZW1wPC1DT1ZJRF8yX0RheV9NZXhpY28gJT4lCiAgaW5uZXJfam9pbihNZXhpY29fVGVtcCxieT1jKCJEYXRlMiI9IkRhdGUiKSkKCkNPVklEX0RheV9zZXJpZXNfTWV4aWNvX1RlbXA8LXh0cyhDT1ZJRF8yX0RheV9NZXhpY29fVGVtcCRXb3JsZF9jb25maXJtZWQsIG9yZGVyLmJ5PUNPVklEXzJfRGF5X01leGljb19UZW1wJERhdGUyKQpgYGAKCmBgYHtyfQpDT1ZJRF9zZXJpZXNfTWV4aWNvX1RyYWluPC1DT1ZJRF9EYXlfc2VyaWVzX01leGljb19UZW1wWzE6NzRdICNVbnRpbCBBcHJpbCA0dGgKQ09WSURfc2VyaWVzX01leGljb19UZXN0PC1DT1ZJRF9EYXlfc2VyaWVzX01leGljb19UZW1wWzc1Omxlbmd0aChDT1ZJRF9EYXlfc2VyaWVzX01leGljb19UZW1wKV0gI0Zyb20gQXByaWwgNXRoIG9ud2FyZHMKYGBgCgpHZXQgdGhlIGxhZ2dlZCBkaWZmZXJlbmNlOgpgYGB7cn0KcGxvdChkaWZmKENPVklEX3Nlcmllc19NZXhpY29fVHJhaW4pLHR5cGU9ImwiLG1haW49IjFzdCAiKSAKYGBgCgpDb3JyZWxvZ3JhbXMgb2YgZmlyc3QgZGlmZXJlbmNlOgpgYGB7cn0KdHNkaXNwbGF5KGRpZmYoQ09WSURfc2VyaWVzX01leGljb19UcmFpbikpCmBgYAoKQXJpbWEgbW9kZWw6IEFSSU1BKDIsMiwzKQpgYGB7cn0KI0F1dG8gQXJpbWEgZm9yIE1leGljbzoKCiNBUklNQTFfTWV4aWNvPC1hdXRvLmFyaW1hKENPVklEX3Nlcmllc19NZXhpY29fVHJhaW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI3hyZWcgPSBDT1ZJRF8yX0RheV9NZXhpY29fVGVtcCRUZW1wZXJhdHVyZV9NZXhpY29bMTo3NF0pCkFSSU1BMV9NZXhpY288LUFyaW1hKENPVklEX3Nlcmllc19NZXhpY29fVHJhaW4sb3JkZXI9YygyLDIsMykseHJlZz1DT1ZJRF8yX0RheV9NZXhpY29fVGVtcCRUZW1wZXJhdHVyZV9NZXhpY29bMTo3NF0pCnN1bW1hcnkoQVJJTUExX01leGljbykKYGBgCgpGb3JlY2FzdCBwcmVkaWN0aW9uIHRvIGNvbXBhcmUKYGBge3J9ClRlc3RfTWV4aWNvX1RlbXA8LUNPVklEXzJfRGF5X01leGljb19UZW1wJFRlbXBlcmF0dXJlX01leGljb1s3NTpsZW5ndGgoQ09WSURfRGF5X3Nlcmllc19NZXhpY29fVGVtcCldCgoKUF9NRVhfMTwtZm9yZWNhc3QoQVJJTUExX01leGljbyx4cmVnID0gVGVzdF9NZXhpY29fVGVtcCkKYGBgCgpDYWxjdWxhdGUgZXJyb3JzOgpgYGB7cn0KUkVfTUVYXzE8LVJFKFBfTUVYXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX01leGljb19UZXN0KSkKTUFQRV9NRVhfMTwtTUFQRShQX01FWF8xJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19NZXhpY29fVGVzdCkpCk1TRV9NRVhfMTwtTVNFKFBfTUVYXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX01leGljb19UZXN0KSkKUEZBX01FWF8xPC1QRkEoUF9NRVhfMSRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfTWV4aWNvX1Rlc3QpKQoKRXJyb3JzLk1FWF8xPC1jKFJFX01FWF8xLE1BUEVfTUVYXzEsTVNFX01FWF8xLFBGQV9NRVhfMSkKRXJyb3JzLk1FWF8xCmBgYAoKTWFudWFsIE1BUEU6CmBgYHtyfQpkZDwtZGF0YS5mcmFtZShBY3R1YWw9YXMudmVjdG9yKENPVklEX3Nlcmllc19NZXhpY29fVGVzdCksCiAgICAgICAgICAgICAgIEZvcmU9UF9NRVhfMSRtZWFuCikKZGQ8LWRkICU+JSBtdXRhdGUoQWJzX1Blcl9FcnJvcj1hYnMoQWN0dWFsLUZvcmUpL2FicyhBY3R1YWwpKQptZWFuKGRkJEFic19QZXJfRXJyb3IpKjEwMApgYGAKCipNb2RlbCB3aXRob3V0IHRlbXBlcmF0dXJlKgoKQVJJTUEoMiwyLDQpCmBgYHtyfQojQXV0byBBcmltYSBmb3IgTWV4aWNvOgoKQVJJTUEyX01leGljbzwtYXV0by5hcmltYShDT1ZJRF9zZXJpZXNfTWV4aWNvX1RyYWluKQpzdW1tYXJ5KEFSSU1BMl9NZXhpY28pCmBgYAoKRm9yZWNhc3QgcHJlZGljdGlvbiB0byBjb21wYXJlCmBgYHtyfQpQX01FWF8yPC1mb3JlY2FzdChBUklNQTJfTWV4aWNvLGg9bGVuZ3RoKENPVklEX3Nlcmllc19NZXhpY29fVGVzdCkpCmBgYAoKQ2FsY3VsYXRlIGVycm9yczoKYGBge3J9ClJFX01FWF8yPC1SRShQX01FWF8yJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19NZXhpY29fVGVzdCkpCk1BUEVfTUVYXzI8LU1BUEUoUF9NRVhfMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfTWV4aWNvX1Rlc3QpKQpNU0VfTUVYXzI8LU1TRShQX01FWF8yJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19NZXhpY29fVGVzdCkpClBGQV9NRVhfMjwtUEZBKFBfTUVYXzIkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX01leGljb19UZXN0KSkKCkVycm9ycy5NRVhfMjwtYyhSRV9NRVhfMixNQVBFX01FWF8yLE1TRV9NRVhfMixQRkFfTUVYXzIpCkVycm9ycy5NRVhfMgpgYGAKCmBgYHtyfQpFcnJvcnM8LXJiaW5kKEVycm9ycy5NRVhfMSxFcnJvcnMuTUVYXzIpCgpyb3duYW1lcyhFcnJvcnMpPC1jKCJBUklNQVgoMiwyLDMpIiwiQVJJTUEoMiwyLDQpIikKCmNvbG5hbWVzKEVycm9ycyk8LWMoIlJlbGF0aXZlIGVycm9yIiwiTWVhbiBBYnMgJSBlcnJvciIsIk1lYW4gU3F1YXJlZCBFcnJvciIsIlBGQSIpCgpFcnJvcnM8LWFzLmRhdGEuZnJhbWUoRXJyb3JzKQoKbWF4aW11bTwtYXBwbHkoRXJyb3JzLDIsbWF4KQoKbWluaW11bTwtYXBwbHkoRXJyb3JzLDIsbWluKQoKRXJyb3JzPC1yYmluZChtaW5pbXVtLEVycm9ycykKCkVycm9yczwtcmJpbmQobWF4aW11bSxFcnJvcnMpCmBgYAoKYGBge3J9CnJhZGFyY2hhcnQoRXJyb3JzLG1heG1pbj1UUlVFLGF4aXN0eXBlPTQsYXhpc2xhYmNvbD0ic2xhdGVncmF5NCIsCiAgICAgICAgICAgY2VudGVyemVybz1GQUxTRSxzZWc9OCxjZ2xjb2w9ImdyYXk2NyIsCiAgICAgICAgICAgcGNvbD1jKCJkb2RnZXJibHVlMiIsImZpcmVicmljazIiLCJkYXJrb3JhbmdlMiIsImRhcmtvcmNoaWQyIiksCiAgICAgICAgICAgcGx0eT0xLAogICAgICAgICAgIHBsd2Q9MywKICAgICAgICAgICB0aXRsZT0iRXJyb3IgY29tcGFyaXNvbiIpCgpsZWdlbmQgPC1sZWdlbmQoMS41LDEsIGxlZ2VuZD1jKCJBUklNQVgoMiwyLDMpIiwiQVJJTUEoMiwyLDQpIiksCiAgICAgICAgICAgICAgICAgc2VnLmxlbj0tMS40LAogICAgICAgICAgICAgICAgIHRpdGxlPSJFcnJvcnMiLAogICAgICAgICAgICAgICAgIHBjaD0yMSwgCiAgICAgICAgICAgICAgICAgYnR5PSJuIiAsbHdkPTMsIHkuaW50ZXJzcD0xLCBob3Jpej1GQUxTRSwKICAgICAgICAgICAgICAgICBjb2w9YygiZG9kZ2VyYmx1ZTIiLCJmaXJlYnJpY2syIiwiZGFya29yYW5nZTIiLCJkYXJrb3JjaGlkMiIpKQpgYGAKCkNvbmNsdXNpb246IEtlZXAgbW9kZWwgd2l0aCB0ZW1wZXJhdHVyZQoKTWFrZSBtb2RlbCB3aXRoIHRoZSBvdmVyYWxsIHNlcmllcwpgYGB7cn0KI0F1dG8gQXJpbWEgZm9yIE1leGljbzoKCkFSSU1BX0ZpbmFsX01leGljbzwtYXV0by5hcmltYShDT1ZJRF9EYXlfc2VyaWVzX01leGljb19UZW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhyZWcgPSBDT1ZJRF8yX0RheV9NZXhpY29fVGVtcCRUZW1wZXJhdHVyZV9NZXhpY28pCnN1bW1hcnkoQVJJTUFfRmluYWxfTWV4aWNvKQpgYGAKCkZpbmFsIGZvcmVjYXN0OgpgYGB7cn0KRnV0dXJlX01leGljb19UZW1wPC1NZXhpY29fVGVtcCRUZW1wZXJhdHVyZV9NZXhpY29bTWV4aWNvX1RlbXAkRGF0ZT5tYXgoQ09WSURfMiREYXRlMildCgpQX01FWF9GaW5hbDwtZm9yZWNhc3QoQVJJTUFfRmluYWxfTWV4aWNvLHhyZWcgPSBGdXR1cmVfTWV4aWNvX1RlbXApCkxvd19saW1fTUVYPC1kYXRhLmZyYW1lKFBfTUVYX0ZpbmFsJGxvd2VyKVssMl0KVXBwX2xpbV9NRVg8LWRhdGEuZnJhbWUoUF9NRVhfRmluYWwkdXBwZXIpWywyXQpgYGAKCkZvciBtYWtpbmcgdGhlIHBsb3Q6CmBgYHtyfQojI0RhdGEgcGVyaW9kcwpwZXJfMSA8LSBhcy5EYXRlKGFzLmNoYXJhY3RlcihDT1ZJRF8yX0RheV9NZXhpY29fVGVtcCREYXRlMikpCnBlcl8yIDwtIHNlcShhcy5EYXRlKG1heChDT1ZJRF8yX0RheV9NZXhpY29fVGVtcCREYXRlMikrMSxmb3JtYXQ9IiVZLSVtLSVkIiksIGFzLkRhdGUoIjIwMjAtMDQtMzAiLGZvcm1hdD0iJVktJW0tJWQiKSwiMSBkYXkiKQoKCiMgTWVyZ2UgZm9yZWNhc3QgKyBhY3R1YWxzCmRhdGEgPC0geHRzKENPVklEX0RheV9zZXJpZXNfTWV4aWNvX1RlbXAsb3JkZXIuYnk9cGVyXzEpIApkYXRhTkEgPC0gcmVwKE5BLCBsZW5ndGgoZGF0YSkpCkEgPC0gY2JpbmQoZGF0YSxkYXRhTkEsZGF0YU5BLGRhdGFOQSkKCgpMb3dfbGltX01FWCA8LSB4dHMoTG93X2xpbV9NRVgsb3JkZXIuYnk9cGVyXzIpCkZvcmVjYXN0X01FWCA8LSB4dHMoUF9NRVhfRmluYWwkbWVhbixvcmRlci5ieT1wZXJfMikKVXBwX2xpbV9NRVggPC0geHRzKFVwcF9saW1fTUVYLG9yZGVyLmJ5PXBlcl8yKQpwcmVkTkEgPC0gcmVwKE5BLCBsZW5ndGgoRm9yZWNhc3RfTUVYKSkKQiA8LSBjYmluZChwcmVkTkEsIExvd19saW1fTUVYLCBGb3JlY2FzdF9NRVgsIFVwcF9saW1fTUVYKQoKYWxsX3Nlcmllc19NRVggPC0gZGF0YS5mcmFtZShyYmluZChhcy5tYXRyaXgoQSksYXMubWF0cml4KEIpKSkKY29sbmFtZXMoYWxsX3Nlcmllc19NRVgpIDwtIGMoJ0FjdHVhbCcsICdMb3dlcl9saW1pdCcsICdGb3JlY2FzdCcsICdVcHBlcl9saW1pdCcpCmBgYAoKYGBge3J9CmR5Z3JhcGgoYWxsX3Nlcmllc19NRVgsIG1haW49IlNBUlMtQ09WMi1vdXRicmVhazogVG90YWwgTWV4aWNvIGNhc2VzIix4bGFiPSJEYXRlIiwgeWxhYj0iTm92ZWwgY29yb25hdmlydXMgY2FzZXMiLHdpZHRoID0gNzUwKSU+JQogIGR5U2VyaWVzKGMoJ0xvd2VyX2xpbWl0JywgJ0ZvcmVjYXN0JywgJ1VwcGVyX2xpbWl0JyksbGFiZWw9IkZvcmVjYXN0IixzdHJva2VXaWR0aD0yLAogICAgICAgICAgIGRyYXdQb2ludHMgPSBUUlVFLCBwb2ludFNpemUgPSAyLCBjb2xvcj1yZ2IoMTg5LzI1NSw0NC8yNTUsNDcvMjU1KSkgJT4lCiAgZHlTZXJpZXMoIkFjdHVhbCIsZHJhd1BvaW50cyA9IFRSVUUsIHN0cm9rZVdpZHRoPTIsIHBvaW50U2l6ZSA9IDIsCiAgICAgICAgICAgY29sb3I9cmdiKDQzLzI1NSwxMDIvMjU1LDczLzI1NSkpICU+JSAKICBkeVJhbmdlU2VsZWN0b3IoKQoKYGBgCgojIyBJdGFseQoKIVtdKEl0YWx5RmxhZy5wbmcpe3dpZHRoPTQwJX0KCipNb2RlbCB3aXRoIHRlbXBlcmF0dXJlKgoKYGBge3J9Ckl0YWx5X1RlbXA8LXJlYWQuY3N2KCJJdGFseV9UZW1wZXJhdHVyZS5jc3YiLHNlcD0iOyIpCkl0YWx5X1RlbXAkRGF0ZTwtYXMuRGF0ZShJdGFseV9UZW1wJERhdGUsZm9ybWF0PSIlZC8lbS8lWSIpCmBgYAoKYGBge3J9CkNPVklEXzJfRGF5X0l0YWx5PC0gQ09WSURfMiAlPiUgCiAgZmlsdGVyKENvdW50cnkuUmVnaW9uICVpbiUgYygiSXRhbHkiKSkgJT4lIAogIGdyb3VwX2J5KERhdGUyKSAlPiUgc3VtbWFyaXNlKFdvcmxkX2NvbmZpcm1lZD1zdW0oQ29uZmlybWVkKSkKCkNPVklEXzJfRGF5X0l0YWx5X1RlbXA8LUNPVklEXzJfRGF5X0l0YWx5ICU+JQogIGlubmVyX2pvaW4oSXRhbHlfVGVtcCxieT1jKCJEYXRlMiI9IkRhdGUiKSkKCkNPVklEX0RheV9zZXJpZXNfSXRhbHlfVGVtcDwteHRzKENPVklEXzJfRGF5X0l0YWx5X1RlbXAkV29ybGRfY29uZmlybWVkLCBvcmRlci5ieT1DT1ZJRF8yX0RheV9JdGFseV9UZW1wJERhdGUyKQpgYGAKCmBgYHtyfQpDT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1RyYWluPC1DT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1RlbXBbMTo2NF0gI1VudGlsIE1hcmNoIDI1dGgKQ09WSURfRGF5X3Nlcmllc19JdGFseV9UZXN0PC1DT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1RlbXBbNjU6bGVuZ3RoKENPVklEX0RheV9zZXJpZXNfSXRhbHlfVGVtcCldICNGcm9tIE1hcmNoIDI2dGggb253YXJkcwpgYGAKCkdldCB0aGUgbGFnZ2VkIGRpZmZlcmVuY2U6CmBgYHtyfQpwbG90KGRpZmYoQ09WSURfRGF5X3Nlcmllc19JdGFseV9UcmFpbiksdHlwZT0ibCIsbWFpbj0iMXN0ICIpIApgYGAKCkNvcnJlbG9ncmFtcyBvZiBmaXJzdCBkaWZlcmVuY2U6CmBgYHtyfQp0c2Rpc3BsYXkoZGlmZihDT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1RyYWluKSkKYGBgCgpBcmltYSBtb2RlbDogQVJJTUEoMSwyLDApCmBgYHtyfQojQXV0byBBcmltYSBmb3IgSXRhbHk6CgpBUklNQTFfSXRhbHk8LWF1dG8uYXJpbWEoQ09WSURfRGF5X3Nlcmllc19JdGFseV9UcmFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4cmVnID0gQ09WSURfMl9EYXlfSXRhbHlfVGVtcCRUZW1wZXJhdHVyZV9JdGFseVsxOjY0XSkKI0FSSU1BMV9NZXhpY288LUFyaW1hKENPVklEX3Nlcmllc19NZXhpY29fVHJhaW4sb3JkZXI9YygyLDIsMykseHJlZz1DT1ZJRF8yX0RheV9NZXhpY29fVGVtcCRUZW1wZXJhdHVyZV9NZXhpY29bMTo3NF0pCnN1bW1hcnkoQVJJTUExX0l0YWx5KQpgYGAKCkZvcmVjYXN0IHByZWRpY3Rpb24gdG8gY29tcGFyZQpgYGB7cn0KVGVzdF9JdGFseV9UZW1wPC1DT1ZJRF8yX0RheV9JdGFseV9UZW1wJFRlbXBlcmF0dXJlX0l0YWx5WzY1Omxlbmd0aChDT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1RlbXApXQoKClBfSVRBXzE8LWZvcmVjYXN0KEFSSU1BMV9JdGFseSx4cmVnID0gVGVzdF9JdGFseV9UZW1wKQpgYGAKCkNhbGN1bGF0ZSBlcnJvcnM6CmBgYHtyfQpSRV9JVEFfMTwtUkUoUF9JVEFfMSRtZWFuLGFzLnZlY3RvcihDT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1Rlc3QpKQpNQVBFX0lUQV8xPC1NQVBFKFBfSVRBXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfRGF5X3Nlcmllc19JdGFseV9UZXN0KSkKTVNFX0lUQV8xPC1NU0UoUF9JVEFfMSRtZWFuLGFzLnZlY3RvcihDT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1Rlc3QpKQpQRkFfSVRBXzE8LVBGQShQX0lUQV8xJG1lYW4sYXMudmVjdG9yKENPVklEX0RheV9zZXJpZXNfSXRhbHlfVGVzdCkpCgpFcnJvcnMuSVRBXzE8LWMoUkVfSVRBXzEsTUFQRV9JVEFfMSxNU0VfSVRBXzEsUEZBX0lUQV8xKQpFcnJvcnMuSVRBXzEKYGBgCgpNYW51YWwgTUFQRToKYGBge3J9CmRkPC1kYXRhLmZyYW1lKEFjdHVhbD1hcy52ZWN0b3IoQ09WSURfRGF5X3Nlcmllc19JdGFseV9UZXN0KSwKICAgICAgICAgICAgICAgRm9yZT1QX0lUQV8xJG1lYW4KKQpkZDwtZGQgJT4lIG11dGF0ZShBYnNfUGVyX0Vycm9yPWFicyhBY3R1YWwtRm9yZSkvYWJzKEFjdHVhbCkpCm1lYW4oZGQkQWJzX1Blcl9FcnJvcikqMTAwCmBgYAoKKk1vZGVsIHdpdGhvdXQgdGVtcGVyYXR1cmUqCgpBUklNQSgxLDIsMCkKYGBge3J9CiNBdXRvIEFyaW1hIGZvciBJdGFseToKQVJJTUEyX0l0YWx5PC1hdXRvLmFyaW1hKENPVklEX0RheV9zZXJpZXNfSXRhbHlfVHJhaW4pCnN1bW1hcnkoQVJJTUEyX0l0YWx5KQpgYGAKCkZvcmVjYXN0IHByZWRpY3Rpb24gdG8gY29tcGFyZQpgYGB7cn0KUF9JVEFfMjwtZm9yZWNhc3QoQVJJTUEyX0l0YWx5LGg9bGVuZ3RoKENPVklEX0RheV9zZXJpZXNfSXRhbHlfVGVzdCkpCmBgYAoKQ2FsY3VsYXRlIGVycm9yczoKYGBge3J9ClJFX0lUQV8yPC1SRShQX0lUQV8yJG1lYW4sYXMudmVjdG9yKENPVklEX0RheV9zZXJpZXNfSXRhbHlfVGVzdCkpCk1BUEVfSVRBXzI8LU1BUEUoUF9JVEFfMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1Rlc3QpKQpNU0VfSVRBXzI8LU1TRShQX0lUQV8yJG1lYW4sYXMudmVjdG9yKENPVklEX0RheV9zZXJpZXNfSXRhbHlfVGVzdCkpClBGQV9JVEFfMjwtUEZBKFBfSVRBXzIkbWVhbixhcy52ZWN0b3IoQ09WSURfRGF5X3Nlcmllc19JdGFseV9UZXN0KSkKCkVycm9ycy5JVEFfMjwtYyhSRV9JVEFfMixNQVBFX0lUQV8yLE1TRV9JVEFfMixQRkFfSVRBXzIpCkVycm9ycy5JVEFfMgpgYGAKCmBgYHtyfQpFcnJvcnM8LXJiaW5kKEVycm9ycy5JVEFfMSxFcnJvcnMuSVRBXzIpCgpyb3duYW1lcyhFcnJvcnMpPC1jKCJBUklNQVgoMSwyLDApIiwiQVJJTUEoMSwyLDApIikKCmNvbG5hbWVzKEVycm9ycyk8LWMoIlJlbGF0aXZlIGVycm9yIiwiTWVhbiBBYnMgJSBlcnJvciIsIk1lYW4gU3F1YXJlZCBFcnJvciIsIlBGQSIpCgpFcnJvcnM8LWFzLmRhdGEuZnJhbWUoRXJyb3JzKQoKbWF4aW11bTwtYXBwbHkoRXJyb3JzLDIsbWF4KQoKbWluaW11bTwtYXBwbHkoRXJyb3JzLDIsbWluKQoKRXJyb3JzPC1yYmluZChtaW5pbXVtLEVycm9ycykKCkVycm9yczwtcmJpbmQobWF4aW11bSxFcnJvcnMpCmBgYAoKYGBge3J9CnJhZGFyY2hhcnQoRXJyb3JzLG1heG1pbj1UUlVFLGF4aXN0eXBlPTQsYXhpc2xhYmNvbD0ic2xhdGVncmF5NCIsCiAgICAgICAgICAgY2VudGVyemVybz1GQUxTRSxzZWc9OCxjZ2xjb2w9ImdyYXk2NyIsCiAgICAgICAgICAgcGNvbD1jKCJkb2RnZXJibHVlMiIsImZpcmVicmljazIiLCJkYXJrb3JhbmdlMiIsImRhcmtvcmNoaWQyIiksCiAgICAgICAgICAgcGx0eT0xLAogICAgICAgICAgIHBsd2Q9MywKICAgICAgICAgICB0aXRsZT0iRXJyb3IgY29tcGFyaXNvbiIpCgpsZWdlbmQgPC1sZWdlbmQoMS41LDEsIGxlZ2VuZD1jKCJBUklNQVgoMSwyLDApIiwiQVJJTUEoMSwyLDApIiksCiAgICAgICAgICAgICAgICAgc2VnLmxlbj0tMS40LAogICAgICAgICAgICAgICAgIHRpdGxlPSJFcnJvcnMiLAogICAgICAgICAgICAgICAgIHBjaD0yMSwgCiAgICAgICAgICAgICAgICAgYnR5PSJuIiAsbHdkPTMsIHkuaW50ZXJzcD0xLCBob3Jpej1GQUxTRSwKICAgICAgICAgICAgICAgICBjb2w9YygiZG9kZ2VyYmx1ZTIiLCJmaXJlYnJpY2syIiwiZGFya29yYW5nZTIiLCJkYXJrb3JjaGlkMiIpKQpgYGAKCkNvbmNsdXNpb246IEtlZXAgbW9kZWwgd2l0aG91dCB0ZW1wZXJhdHVyZQoKTWFrZSBtb2RlbCB3aXRoIHRoZSBvdmVyYWxsIHNlcmllcwpgYGB7cn0KI0F1dG8gQXJpbWEgZm9yIEl0YWx5OgoKQVJJTUFfRmluYWxfSXRhbHk8LWF1dG8uYXJpbWEoQ09WSURfRGF5X3Nlcmllc19JdGFseV9UZW1wKQpzdW1tYXJ5KEFSSU1BX0ZpbmFsX0l0YWx5KQpgYGAKCkZpbmFsIGZvcmVjYXN0OgpgYGB7cn0KRnV0dXJlX0l0YWx5X1RlbXA8LUl0YWx5X1RlbXAkVGVtcGVyYXR1cmVfSXRhbHlbSXRhbHlfVGVtcCREYXRlPm1heChDT1ZJRF8yJERhdGUyKV0KClBfSVRBX0ZpbmFsPC1mb3JlY2FzdChBUklNQV9GaW5hbF9JdGFseSxoPWxlbmd0aChGdXR1cmVfSXRhbHlfVGVtcCkpCkxvd19saW1fSVRBPC1kYXRhLmZyYW1lKFBfSVRBX0ZpbmFsJGxvd2VyKVssMl0KVXBwX2xpbV9JVEE8LWRhdGEuZnJhbWUoUF9JVEFfRmluYWwkdXBwZXIpWywyXQpgYGAKCkZvciBtYWtpbmcgdGhlIHBsb3Q6CmBgYHtyfQojI0RhdGEgcGVyaW9kcwpwZXJfMSA8LSBhcy5EYXRlKGFzLmNoYXJhY3RlcihDT1ZJRF8yX0RheV9JdGFseV9UZW1wJERhdGUyKSkKcGVyXzIgPC0gc2VxKGFzLkRhdGUobWF4KENPVklEXzJfRGF5X0l0YWx5X1RlbXAkRGF0ZTIpKzEsZm9ybWF0PSIlWS0lbS0lZCIpLCBhcy5EYXRlKCIyMDIwLTA0LTMwIixmb3JtYXQ9IiVZLSVtLSVkIiksIjEgZGF5IikKCgojIE1lcmdlIGZvcmVjYXN0ICsgYWN0dWFscwpkYXRhIDwtIHh0cyhDT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1RlbXAsb3JkZXIuYnk9cGVyXzEpIApkYXRhTkEgPC0gcmVwKE5BLCBsZW5ndGgoZGF0YSkpCkEgPC0gY2JpbmQoZGF0YSxkYXRhTkEsZGF0YU5BLGRhdGFOQSkKCgpMb3dfbGltX0lUQSA8LSB4dHMoTG93X2xpbV9JVEEsb3JkZXIuYnk9cGVyXzIpCkZvcmVjYXN0X0lUQSA8LSB4dHMoUF9JVEFfRmluYWwkbWVhbixvcmRlci5ieT1wZXJfMikKVXBwX2xpbV9JVEEgPC0geHRzKFVwcF9saW1fSVRBLG9yZGVyLmJ5PXBlcl8yKQpwcmVkTkEgPC0gcmVwKE5BLCBsZW5ndGgoRm9yZWNhc3RfSVRBKSkKQiA8LSBjYmluZChwcmVkTkEsIExvd19saW1fSVRBLCBGb3JlY2FzdF9JVEEsIFVwcF9saW1fSVRBKQoKYWxsX3Nlcmllc19JVEEgPC0gZGF0YS5mcmFtZShyYmluZChhcy5tYXRyaXgoQSksYXMubWF0cml4KEIpKSkKY29sbmFtZXMoYWxsX3Nlcmllc19JVEEpIDwtIGMoJ0FjdHVhbCcsICdMb3dlcl9saW1pdCcsICdGb3JlY2FzdCcsICdVcHBlcl9saW1pdCcpCmBgYAoKYGBge3J9CmR5Z3JhcGgoYWxsX3Nlcmllc19JVEEsIG1haW49IlNBUlMtQ09WMi1vdXRicmVhazogVG90YWwgSXRhbHkgY2FzZXMiLHhsYWI9IkRhdGUiLCB5bGFiPSJOb3ZlbCBjb3JvbmF2aXJ1cyBjYXNlcyIsd2lkdGggPSA3NTApJT4lCiAgZHlTZXJpZXMoYygnTG93ZXJfbGltaXQnLCAnRm9yZWNhc3QnLCAnVXBwZXJfbGltaXQnKSxsYWJlbD0iRm9yZWNhc3QiLHN0cm9rZVdpZHRoPTIsCiAgICAgICAgICAgZHJhd1BvaW50cyA9IFRSVUUsIHBvaW50U2l6ZSA9IDIsIGNvbG9yPXJnYigxOTAvMjU1LDU5LzI1NSw2MS8yNTUpKSAlPiUKICBkeVNlcmllcygiQWN0dWFsIixkcmF3UG9pbnRzID0gVFJVRSwgc3Ryb2tlV2lkdGg9MiwgcG9pbnRTaXplID0gMiwKICAgICAgICAgICBjb2xvcj1yZ2IoNjQvMjU1LDE0My8yNTUsNzgvMjU1KSkgJT4lIAogIGR5UmFuZ2VTZWxlY3RvcigpCgpgYGAKCgojIyBMZWJhbm9uCgohW10oTGViYW5vbkZsYWcucG5nKXt3aWR0aD00MCV9CgoqTW9kZWwgd2l0aCB0ZW1wZXJhdHVyZSoKCmBgYHtyfQpMZWJhbm9uX1RlbXA8LXJlYWQuY3N2KCJMZWJhbm9uX1RlbXBlcmF0dXJlLmNzdiIsc2VwPSI7IikKTGViYW5vbl9UZW1wJERhdGU8LWFzLkRhdGUoTGViYW5vbl9UZW1wJERhdGUsZm9ybWF0PSIlZC8lbS8lWSIpCmBgYAoKYGBge3J9CkNPVklEXzJfRGF5X0xlYmFub248LSBDT1ZJRF8yICU+JSAKICBmaWx0ZXIoQ291bnRyeS5SZWdpb24gJWluJSBjKCJMZWJhbm9uIikpICU+JSAKICBncm91cF9ieShEYXRlMikgJT4lIHN1bW1hcmlzZShXb3JsZF9jb25maXJtZWQ9c3VtKENvbmZpcm1lZCkpCgpDT1ZJRF8yX0RheV9MZWJhbm9uX1RlbXA8LUNPVklEXzJfRGF5X0xlYmFub24gJT4lCiAgaW5uZXJfam9pbihMZWJhbm9uX1RlbXAsYnk9YygiRGF0ZTIiPSJEYXRlIikpCgpDT1ZJRF9EYXlfc2VyaWVzX0xlYmFub25fVGVtcDwteHRzKENPVklEXzJfRGF5X0xlYmFub25fVGVtcCRXb3JsZF9jb25maXJtZWQsIG9yZGVyLmJ5PUNPVklEXzJfRGF5X0xlYmFub25fVGVtcCREYXRlMikKYGBgCgpgYGB7cn0KQ09WSURfc2VyaWVzX0xlYmFub25fVHJhaW48LUNPVklEX0RheV9zZXJpZXNfTGViYW5vbl9UZW1wWzE6NzRdICNVbnRpbCBBcHJpbCA0dGgKQ09WSURfc2VyaWVzX0xlYmFub25fVGVzdDwtQ09WSURfRGF5X3Nlcmllc19MZWJhbm9uX1RlbXBbNzU6bGVuZ3RoKENPVklEX0RheV9zZXJpZXNfTGViYW5vbl9UZW1wKV0gI0Zyb20gQXByaWwgNHRoIG9ud2FyZHMKYGBgCgpHZXQgdGhlIGxhZ2dlZCBkaWZmZXJlbmNlOgpgYGB7cn0KcGxvdChkaWZmKENPVklEX3Nlcmllc19MZWJhbm9uX1RyYWluKSx0eXBlPSJsIixtYWluPSIxc3QgIikgCmBgYAoKQ29ycmVsb2dyYW1zIG9mIGZpcnN0IGRpZmVyZW5jZToKYGBge3J9CnRzZGlzcGxheShkaWZmKENPVklEX3Nlcmllc19MZWJhbm9uX1RyYWluKSkKYGBgCgpBcmltYSBtb2RlbDogQVJJTUEoMSwxLDIpCmBgYHtyfQojQXV0byBBcmltYSBmb3IgTGViYW5vbjoKCkFSSU1BMV9MZWJhbm9uPC1hdXRvLmFyaW1hKENPVklEX3Nlcmllc19MZWJhbm9uX1RyYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhyZWcgPSBDT1ZJRF8yX0RheV9MZWJhbm9uX1RlbXAkVGVtcGVyYXR1cmVfTGViYW5vblsxOjc0XSkKI0FSSU1BMV9NZXhpY288LUFyaW1hKENPVklEX3Nlcmllc19NZXhpY29fVHJhaW4sb3JkZXI9YygyLDIsMykseHJlZz1DT1ZJRF8yX0RheV9NZXhpY29fVGVtcCRUZW1wZXJhdHVyZV9NZXhpY29bMTo3NF0pCnN1bW1hcnkoQVJJTUExX0xlYmFub24pCmBgYAoKRm9yZWNhc3QgcHJlZGljdGlvbiB0byBjb21wYXJlCmBgYHtyfQpUZXN0X0xlYmFub25fVGVtcDwtQ09WSURfMl9EYXlfTGViYW5vbl9UZW1wJFRlbXBlcmF0dXJlX0xlYmFub25bNzU6bGVuZ3RoKENPVklEX0RheV9zZXJpZXNfTGViYW5vbl9UZW1wKV0KCgpQX0xCTl8xPC1mb3JlY2FzdChBUklNQTFfTGViYW5vbix4cmVnID0gVGVzdF9MZWJhbm9uX1RlbXApCmBgYAoKQ2FsY3VsYXRlIGVycm9yczoKYGBge3J9ClJFX0xCTl8xPC1SRShQX0xCTl8xJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19MZWJhbm9uX1Rlc3QpKQpNQVBFX0xCTl8xPC1NQVBFKFBfTEJOXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0xlYmFub25fVGVzdCkpCk1TRV9MQk5fMTwtTVNFKFBfTEJOXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0xlYmFub25fVGVzdCkpClBGQV9MQk5fMTwtUEZBKFBfTEJOXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0xlYmFub25fVGVzdCkpCgpFcnJvcnMuTEJOXzE8LWMoUkVfTEJOXzEsTUFQRV9MQk5fMSxNU0VfTEJOXzEsUEZBX0xCTl8xKQpFcnJvcnMuTEJOXzEKYGBgCgpNYW51YWwgTUFQRToKYGBge3J9CmRkPC1kYXRhLmZyYW1lKEFjdHVhbD1hcy52ZWN0b3IoQ09WSURfc2VyaWVzX0xlYmFub25fVGVzdCksCiAgICAgICAgICAgICAgIEZvcmU9UF9MQk5fMSRtZWFuCikKZGQ8LWRkICU+JSBtdXRhdGUoQWJzX1Blcl9FcnJvcj1hYnMoQWN0dWFsLUZvcmUpL2FicyhBY3R1YWwpKQptZWFuKGRkJEFic19QZXJfRXJyb3IpKjEwMApgYGAKCipNb2RlbCB3aXRob3V0IHRlbXBlcmF0dXJlKgoKQVJJTUEoMCwyLDIpCmBgYHtyfQojQXV0byBBcmltYSBmb3IgTGViYW5vbjoKQVJJTUEyX0xlYmFub248LWF1dG8uYXJpbWEoQ09WSURfc2VyaWVzX0xlYmFub25fVHJhaW4pCnN1bW1hcnkoQVJJTUEyX0xlYmFub24pCmBgYAoKRm9yZWNhc3QgcHJlZGljdGlvbiB0byBjb21wYXJlCmBgYHtyfQpQX0xCTl8yPC1mb3JlY2FzdChBUklNQTJfTGViYW5vbixoPWxlbmd0aChDT1ZJRF9zZXJpZXNfTGViYW5vbl9UZXN0KSkKYGBgCgpDYWxjdWxhdGUgZXJyb3JzOgpgYGB7cn0KUkVfTEJOXzI8LVJFKFBfTEJOXzIkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0xlYmFub25fVGVzdCkpCk1BUEVfTEJOXzI8LU1BUEUoUF9MQk5fMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfTGViYW5vbl9UZXN0KSkKTVNFX0xCTl8yPC1NU0UoUF9MQk5fMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfTGViYW5vbl9UZXN0KSkKUEZBX0xCTl8yPC1QRkEoUF9MQk5fMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfTGViYW5vbl9UZXN0KSkKCkVycm9ycy5MQk5fMjwtYyhSRV9MQk5fMixNQVBFX0xCTl8yLE1TRV9MQk5fMixQRkFfTEJOXzIpCkVycm9ycy5MQk5fMgpgYGAKCmBgYHtyfQpFcnJvcnM8LXJiaW5kKEVycm9ycy5MQk5fMSxFcnJvcnMuTEJOXzIpCgpyb3duYW1lcyhFcnJvcnMpPC1jKCJBUklNQVgoMSwxLDIpIiwiQVJJTUEoMCwyLDIpIikKCmNvbG5hbWVzKEVycm9ycyk8LWMoIlJlbGF0aXZlIGVycm9yIiwiTWVhbiBBYnMgJSBlcnJvciIsIk1lYW4gU3F1YXJlZCBFcnJvciIsIlBGQSIpCgpFcnJvcnM8LWFzLmRhdGEuZnJhbWUoRXJyb3JzKQoKbWF4aW11bTwtYXBwbHkoRXJyb3JzLDIsbWF4KQoKbWluaW11bTwtYXBwbHkoRXJyb3JzLDIsbWluKQoKRXJyb3JzPC1yYmluZChtaW5pbXVtLEVycm9ycykKCkVycm9yczwtcmJpbmQobWF4aW11bSxFcnJvcnMpCmBgYAoKYGBge3J9CnJhZGFyY2hhcnQoRXJyb3JzLG1heG1pbj1UUlVFLGF4aXN0eXBlPTQsYXhpc2xhYmNvbD0ic2xhdGVncmF5NCIsCiAgICAgICAgICAgY2VudGVyemVybz1GQUxTRSxzZWc9OCxjZ2xjb2w9ImdyYXk2NyIsCiAgICAgICAgICAgcGNvbD1jKCJkb2RnZXJibHVlMiIsImZpcmVicmljazIiLCJkYXJrb3JhbmdlMiIsImRhcmtvcmNoaWQyIiksCiAgICAgICAgICAgcGx0eT0xLAogICAgICAgICAgIHBsd2Q9MywKICAgICAgICAgICB0aXRsZT0iRXJyb3IgY29tcGFyaXNvbiIpCgpsZWdlbmQgPC1sZWdlbmQoMS41LDEsIGxlZ2VuZD1jKCJBUklNQVgoMSwxLDIpIiwiQVJJTUEoMCwyLDIpIiksCiAgICAgICAgICAgICAgICAgc2VnLmxlbj0tMS40LAogICAgICAgICAgICAgICAgIHRpdGxlPSJFcnJvcnMiLAogICAgICAgICAgICAgICAgIHBjaD0yMSwgCiAgICAgICAgICAgICAgICAgYnR5PSJuIiAsbHdkPTMsIHkuaW50ZXJzcD0xLCBob3Jpej1GQUxTRSwKICAgICAgICAgICAgICAgICBjb2w9YygiZG9kZ2VyYmx1ZTIiLCJmaXJlYnJpY2syIiwiZGFya29yYW5nZTIiLCJkYXJrb3JjaGlkMiIpKQpgYGAKCkNvbmNsdXNpb246IEtlZXAgbW9kZWwgd2l0aG91dCB0ZW1wZXJhdHVyZQoKTWFrZSBtb2RlbCB3aXRoIHRoZSBvdmVyYWxsIHNlcmllcwpgYGB7cn0KI0F1dG8gQXJpbWEgZm9yIExlYmFub246CgpBUklNQV9GaW5hbF9MZWJhbm9uPC1hdXRvLmFyaW1hKENPVklEX0RheV9zZXJpZXNfTGViYW5vbl9UZW1wKQpzdW1tYXJ5KEFSSU1BX0ZpbmFsX0xlYmFub24pCmBgYAoKRmluYWwgZm9yZWNhc3Q6CmBgYHtyfQpGdXR1cmVfTGViYW5vbl9UZW1wPC1MZWJhbm9uX1RlbXAkVGVtcGVyYXR1cmVfTGViYW5vbltMZWJhbm9uX1RlbXAkRGF0ZT5tYXgoQ09WSURfMiREYXRlMildCgpQX0xCTl9GaW5hbDwtZm9yZWNhc3QoQVJJTUFfRmluYWxfTGViYW5vbixoPWxlbmd0aChGdXR1cmVfTGViYW5vbl9UZW1wKSkKTG93X2xpbV9MQk48LWRhdGEuZnJhbWUoUF9MQk5fRmluYWwkbG93ZXIpWywyXQpVcHBfbGltX0xCTjwtZGF0YS5mcmFtZShQX0xCTl9GaW5hbCR1cHBlcilbLDJdCmBgYAoKRm9yIG1ha2luZyB0aGUgcGxvdDoKYGBge3J9CiMjRGF0YSBwZXJpb2RzCnBlcl8xIDwtIGFzLkRhdGUoYXMuY2hhcmFjdGVyKENPVklEXzJfRGF5X0xlYmFub25fVGVtcCREYXRlMikpCnBlcl8yIDwtIHNlcShhcy5EYXRlKG1heChDT1ZJRF8yX0RheV9MZWJhbm9uX1RlbXAkRGF0ZTIpKzEsZm9ybWF0PSIlWS0lbS0lZCIpLCBhcy5EYXRlKCIyMDIwLTA0LTMwIixmb3JtYXQ9IiVZLSVtLSVkIiksIjEgZGF5IikKCgojIE1lcmdlIGZvcmVjYXN0ICsgYWN0dWFscwpkYXRhIDwtIHh0cyhDT1ZJRF9EYXlfc2VyaWVzX0xlYmFub25fVGVtcCxvcmRlci5ieT1wZXJfMSkgCmRhdGFOQSA8LSByZXAoTkEsIGxlbmd0aChkYXRhKSkKQSA8LSBjYmluZChkYXRhLGRhdGFOQSxkYXRhTkEsZGF0YU5BKQoKCkxvd19saW1fTEJOIDwtIHh0cyhMb3dfbGltX0xCTixvcmRlci5ieT1wZXJfMikKRm9yZWNhc3RfTEJOIDwtIHh0cyhQX0xCTl9GaW5hbCRtZWFuLG9yZGVyLmJ5PXBlcl8yKQpVcHBfbGltX0xCTiA8LSB4dHMoVXBwX2xpbV9MQk4sb3JkZXIuYnk9cGVyXzIpCnByZWROQSA8LSByZXAoTkEsIGxlbmd0aChGb3JlY2FzdF9JVEEpKQpCIDwtIGNiaW5kKHByZWROQSwgTG93X2xpbV9MQk4sIEZvcmVjYXN0X0xCTiwgVXBwX2xpbV9MQk4pCgphbGxfc2VyaWVzX0xCTiA8LSBkYXRhLmZyYW1lKHJiaW5kKGFzLm1hdHJpeChBKSxhcy5tYXRyaXgoQikpKQpjb2xuYW1lcyhhbGxfc2VyaWVzX0xCTikgPC0gYygnQWN0dWFsJywgJ0xvd2VyX2xpbWl0JywgJ0ZvcmVjYXN0JywgJ1VwcGVyX2xpbWl0JykKYGBgCgpgYGB7cn0KZHlncmFwaChhbGxfc2VyaWVzX0xCTiwgbWFpbj0iU0FSUy1DT1YyLW91dGJyZWFrOiBUb3RhbCBMZWJhbm9uIGNhc2VzIix4bGFiPSJEYXRlIiwgeWxhYj0iTm92ZWwgY29yb25hdmlydXMgY2FzZXMiLHdpZHRoID0gNzUwKSU+JQogIGR5U2VyaWVzKGMoJ0xvd2VyX2xpbWl0JywgJ0ZvcmVjYXN0JywgJ1VwcGVyX2xpbWl0JyksbGFiZWw9IkZvcmVjYXN0IixzdHJva2VXaWR0aD0yLAogICAgICAgICAgIGRyYXdQb2ludHMgPSBUUlVFLCBwb2ludFNpemUgPSAyLCBjb2xvcj1yZ2IoMjE4LzI1NSw1NS8yNTUsNTAvMjU1KSkgJT4lCiAgZHlTZXJpZXMoIkFjdHVhbCIsZHJhd1BvaW50cyA9IFRSVUUsIHN0cm9rZVdpZHRoPTIsIHBvaW50U2l6ZSA9IDIsCiAgICAgICAgICAgY29sb3I9cmdiKDczLzI1NSwxNjMvMjU1LDkwLzI1NSkpICU+JSAKICBkeVJhbmdlU2VsZWN0b3IoKQoKYGBgCgoKCgoKCgoKCmBgYHtyfQojRm9yZWNhc3RzPC1saXN0KGFsbF9zZXJpZXNfQ1JJLGFsbF9zZXJpZXNfTUVYLGFsbF9zZXJpZXNfSVRBLGFsbF9zZXJpZXNfTEJOKQojbmFtZXMoRm9yZWNhc3RzKTwtYygiQ29zdGEgUmljYSIsIk1leGljbyIsIkl0YWx5IiwiTGViYW5vbiIpCiNGb3JlY2FzdHMkTWV4aWNvCgojc2F2ZShGb3JlY2FzdHMsIGZpbGU9IkZvcmVjYXN0cy5SRGF0YSIpCmBgYAoKCg==